4.2 JavaEE-MyBatis

目录

4.2.1 MyBatis概述

4.2.1.1 什么是MyBati

4.2.1.2 MyBatis的优缺点

4.2.1.2.1 优点

4.2.1.2.2 缺点

 4.2.1.3 MyBatis与IBatis区别

4.2.1.4 MyBatis与Hibernate区别

4.2.2 全局配置文件

4.2.2.1 properties、environment

4.2.2.2 Mapper映射方式

4.2.2.2.1 什么是Mapper

4.2.2.2.2 映射方式一:resource属性

4.2.2.2.3 映射方式二:class属性

4.2.2.2.4 映射方式三:package属性

4.2.2.3 Mapper

4.2.2.3.1 Select标签

4.2.2.3.2 Insert, Update, Delete标签

4.2.2.3.3 sql、include 标签

主键自增

*4.2.2.3.4 resultMap结果映射

4.2.3 动态SQL标签

4.2.3.1 if 标签

4.2.3.2 where标签

4.2.3.3 trim标签

4.2.3.4 choose、when标签

4.2.3.5 set标签

4.2.3.6 foreach标签

4.2.3.6.1 属性

4.2.3.6.2 批量操作

4.2.4 注解开发

 4.2.4.1 @Select()

 4.2.4.2 @param()

 4.2.4.3  @Insert()

 4.2.4.4  @Update()

 4.2.4.5  @Delete()

 4.2.4.6  @Results()、@Result()

 4.2.4.7  @resultMap()

 4.2.4.8  @One() 一对一查询

 4.2.4.9  @Many() 一对多查询

4.2.5 深入理解MyBatis

4.2.5.1 MyBatis核心组件

4.2.5.1.2 SqlSession

4.2.5.1.3 Executor

4.2.5.1.4 StatementHandler

 4.2.5.1 MyBatis与JDBC

4.2.5.2 MyBatis步骤

4.2.5.3 MyBatis实现分页 

4.2.5.4 MyBatis批处理

4.2.5.4.1 Foreach标签

4.2.5.4.2 BATCH

4.2.5.5 Mapper接口可以重载吗

4.2.5.6 MyBatis一级二级缓存

4.2.5.6.1 三个问题

4.2.5.6.2 一级缓存

4.2.5.6.3 二级缓存

4.2.5.7 Mapper 接口 


4.2.1 MyBatis概述

4.2.1.1 什么是MyBati

ORM(Object-Relationship-Mapping):是对象关系映射的意思,它是一种思想,是指将数据库中的每一行数据用对象的形式表现出来。

简单来说就是,student表中列有id(类型int),name(类型String),age(类型int)。ORM思想就是创建student的javabean类,student类属性有int  id;String name;int age;

JPA(Java-Persistence-API):是Java持久化接口的意思,它是JavaEE关于 ORM 思想的一套标准接口,仅仅是一套接口,不是具体的实现。 早期有 2 个著名的 ORM 映射框架,它们就是 Hibernate 和 Ibatis

  1.  MyBatis的前身就是iBatis,轻量级持久成框架
  2. Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
  3. MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
  4. 通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返 回 result 的过程)。 

4.2.1.2 MyBatis的优缺点

4.2.1.2.1 优点

  1. 基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。
  2. 与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接;
  3. 很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。
  4. 能够与 Spring 很好的集成;
  5. 提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射 标签,支持对象关系组件维护。

4.2.1.2.2 缺点

  1. SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求。
  2. SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

 4.2.1.3 MyBatis与IBatis区别

MyBatis 增加了接口绑定

MyBatis 动态sql由原来的节点配置变成了OGNL表达式

MyBatis 一对一引入了association,一对多引入collection

IBatis的核心处理类是SqlMapClient,MyBatis核心处理类是SqlSession。

4.2.1.4 MyBatis与Hibernate区别

Hibernate:

  • JPA   
  • 完全ORM,sql语句不需要自己书写
  • 使用 Hibernate ,可以通过一套程序搞定所有关系型数据库,可以用最小的代价完成关系型数据库的替换。但是完全的封装造成的就是效率不高,调优困难。
  • 因为封装的问题,在数据库操作方面,给出了5套解决方案,操作上变得繁琐。
  • 有公司会使用的 Spring-Data-JPA 技术便是基于Hibernate的实现
  • Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率

使用Hibernate可以对对象与对象的关系映射设计的好

MyBatis:

  • 没有实现JPA   
  •  半个 ORM
  • 编写原生的SQL,效率高
  • 甚至可以不用管对象与对象的关系映射设计
  • Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常 适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需 求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性, 如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大

总的来说:

Hibernate是全ORM框架,mybatis是半ORM框架,

Hibernate实现了JPA接口,操作数据库无需书写sql语句

Mybatis没有实现JPA接口,需要书写原生的sql语句进行数据库操作

Hibernate在sql优化方面以及书写效率存在问题(相比较Mybatis)

Hibernate内部封装完善,学习成本高

4.2.2 全局配置文件

mybatis-config.xml是系统的核心配置文档(全局配置文档),文档格式如下

4.2 JavaEE-MyBatis_第1张图片

由于约束的存在,全局配置的顺序必须要以上要求按顺序书写。

其他的配置参数涉及到mybatis底层配置,不用配置,默认即可。

重点是配置properties,environmentmapper

4.2.2.1 properties、environment

如果单独定义jdbc.properties,则按如下格式搭配

environment的default可以任意取,但是id要与default一致。

4.2 JavaEE-MyBatis_第2张图片

4.2 JavaEE-MyBatis_第3张图片

4.2.2.2 Mapper映射方式

4.2.2.2.1 什么是Mapper

Mapper:映射器:

  1. 用来在mybatis初始化的时候,告诉mybatis需要引入哪些Mapper映射文件
  2. mapper逐个注册SQL映射文件

 通常命名格式为:类名Mapper.xml或类名Dao.xml

Mapper有三种映射方式

4.2.2.2.2 映射方式一:resource属性

resource属性:单独引入相应xml配置(可以通过mapper配置中的namespace与id动态执行)

mybatis-config.xml




    
        
            
            

                
                
                
                
            
        
    


    
        
    

UserMapper.xml





  
    


//自己定义了一个连接数据库的工具类,主要是方便
 class MyBatisUtil {
    private MyBatisUtil(){}
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsStream("mybatis-config.xml"));

        } catch (Exception e) {

        }
    }
        public static SqlSessionFactory getSqlSessionFactory(){
            return sqlSessionFactory;
        }

}
 
//通过单元测试来运行   @Test 
public class MyBatisDemo {
    @Test
    public void fi(){
        SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
        SqlSession sqlSession = factory.openSession(true);
        List users=sqlSession.selectList("a.selectAll");
        for (User user : users) {
            System.out.println(user);
        }
        //5、关闭资源
        sqlSession.close();


    }

    }


Resources.getResourceAsStream("mybatis-config.xml")读取到全局配置文件信息,然后调用自带的selectList("a.selectAll")方法

4.2.2.2.3 映射方式二:class属性

class属性:创建对应mapper接口对象(可以自动扫描与mapper接口名字相同的xml)

会自动识别指定接口对应包下的接口相关的xml配置

注意

1、对应接口的路径要与对应xml路径相同,如由于UserMapper.xml在resources的mapper包下,所以UserMapper接口也需要在Java的mapper包下。

4.2 JavaEE-MyBatis_第4张图片

 在mapper下创建Usermapper接口,

注意,接口名必须和resources/mapper下对应的xml名字一致,否则系统运行报错。

2、引入一个重要术语—接口绑定:在MyBatis中任意定义接口,并把定义的接口和xml里的sql语句绑定。这样我们就可以直接调用接口方法,相比于sqlsession提供的方法,前者可以更加灵活选择和设置。

Usermapper 接口

public interface Usermapper {
       public List selectAll();




}

 mybatis-config.xml




    
        
            
            

                
                
                
                
            
        
    

    

     
    

 Usermapper.xml





    


测试

public class MyBatisDemo {
    @Test
    public void fi(){
        SqlSessionFactory factory = MyBatisUtil.getSqlSessionFactory();
        SqlSession sqlSession = factory.openSession(true);
        //调用session的方法获取对应的mapper
        //会自动根据xml生成对应接口的实现类
        Usermapper usermapper=sqlSession.getMapper(Usermapper.class);
        //直接调用对应的方法操作数据库即可
        List users=usermapper.selectAll();
        for (User user : users) {
            System.out.println(user);
        }
        //5、关闭资源
        sqlSession.close();
    }
    }

MyBatis调用Mapper接口要注意:

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

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

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

4、mapper.xml文件中的namespace就是mapper接口的类路径

4.2.2.2.4 映射方式三:package属性

package属性:引入指定包下接口对象(可以自动扫描与mapper接口名字相同的xml,class与xml同一目录)

方式三基于方式二,只是在 mybatis-config.xml文件中吧mappers设置为



    
    

其他的配置与方式二一样。

4.2.2.3 Mapper

MyBatis 的真正强大在于它的映射语句 ,与JDBC相比节省了大量的代码。

mapper中的标签(注意顺序)

4.2 JavaEE-MyBatis_第5张图片

常用操作为增删改查,其他按默认配置即可。

4.2.2.3.1 Select标签

Select标签的属性

属性 描述
id 名称空间唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。

 *select标签重点学习传递参数

传递单个参数

#{参数 }   

参数名随便起,

一般按照如下格式

sex=#{sex}

传递多个参数

#{arg0}、#{arg1}...#{arg n-1}

或者

#{param1}、#{param2}...#{paramn}

使用arg0 arg1 或param1 param2 代表第一个参数与第二个参数
自定义参数类型 #{}  #{}中通过属性名的形式传入对应的值 mybatis会自动调用对应的getter方法获取值;可防止sql注入
 ${}中书写的内容会自动当成传递引用类型数据的属性 调用对应的getter方法获取
List集合

#{list[索引]}

或者

#{collection[索引]}

使用Collection 或list 代表传递的集合数据
通过#{list[索引]}或者#{collection[索引]}的形式 获取对应的数据
Map集合 #{key} 通过指定的key获取value

需要注意一些细节:

如果你只接传多个参数:X.select(1,"张三");   传入id和name

那么mapper.xml中应这样写:

-------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------

如果这样写

-------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------

会报错,原因是id和name没有get方法

但是如果先new 一个对象,如student对象

Student student = new Student(id:1,name:"张三");

X.select(student);

-------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------------------------------------------

就不会报错,因为在student中有相应属性的get方法

后面的学习中很多sql语句都需要注意此细节

  

StudentMapper.xml





    
    
    

    
    

    
    

    
    
    
    


    
    

    
    

    


StudentMapper接口

public interface StudentMapper {
    //查询所有数据
    ArrayList selectAllStudent();

    //根据单个基本类型参数查询数据
    //根据姓名查询数据
    ArrayList selectStudentBySex(String sex);

    //根据多个不同基本类型参数查询数据
    //根据姓名与年龄查询数据
    ArrayList selectStudentByNameAndAge(String name,int age);


    //不能防止sql注入的查询
    //自定义引用类型传递
    ArrayList selectStudentByName(Student s);

    //集合类型数据传递
    //根据名字集合查询指定数据
    ArrayList selectStudentByList(ArrayList list);
    //根据map集合查询指定key对应的数据
    ArrayList selectStudentByMap(HashMap map);

    ArrayList selectStudentByArray(String [] arr);

}

4.2.2.3.2 Insert, Update, Delete标签

insert、update、delete标签的属性一致。

属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。




    

    
    

    //注意,Dao层的insertUser传入的不是变量,而是一个User类,所以这里面才能写类的属性名而非arg0或param1之类的
    
        insert into t_user (name,age,addr)values(#{name},#{age},#{addr})
    

    
        update t_user set sal=#{arg1} where id=#{arg0}
    

    
        delete from t_user where id=#{id}
    


4.2.2.3.3 sql、include 标签

 sql标签 可以被用来定义可重用的 SQL 代码段,可以包含在其他语句中
include标签 可以调用定义的sql标签,可以设置property标签 设置name属性与value属性为sql标签中设置的参数赋值
 refid 属性  引入sql标签的id
   
        select * from t_user
    
    
    

实际开发过程中经常只将查询的列放入sql标签

    id,name,age
    

    

如果要对表起别名 那么列名书写时 就必须使用别名.列名的形式。需要额外定义property标签 name:与sql中${X},中的X一致

value:是对表起的别名

需要注意:定义时使用${}进行sql的拼接 (如果#{}会变成?占位符)


    
        ${alias}.sid,${alias}.sname,${alias}.age,${alias}.phone
    
    

这里有道面试题:模糊查询

LIKE '%${ XXX}%' 可能引起sql注入,不推荐
LIKE "%"#{XXX}"%" #{}解析sql语句时,会在变量外侧自动添加单引号',所以这里%需要用双引号包裹
LIKE CONCAT('%',#{XXX}),'%' 使用CONCAT函数,推荐
使用bind标签 不推荐

主键自增

1.通过属性进行获取

属性 描述
useGeneratedKeys 是否使用自动生成主键,默认为false
keyProperty 实体类中属性名
keyColumn 数据库主键字段名(如果id是第一列可以省略不写)
User类
public class User{
    private String uid;

   ----省略----
}

mysql表格格式
--------------
 uid| 省略----
---------------


keyProperty:类中属性  uid
keyColumn:表中的列 uid
在执行添加后会自动将新增数据的主键赋值给对对应参数的对应属性值 

 
        insert into user (uusername,upassword) values (#{uusername},#{upassword})
    

 2.通过selectKey标签获取 

 该标签可以在原有sql执行的前后进行二次执行

      原理:在添加数据后立刻获取最后添加数据的主键 并赋值给对应的实体类

属性

  resultType 查询主键结果类型,可以不写会自动识别
  order=“BEFORE 或 AFTER” 指定sql执行的顺序
      keyProperty 实体类中属性名
      keyColumn 数据库主键字段名(如果id是第一列可以省略不写)

   

  
       
           select LAST_INSERT_ID() as id
       
        insert into student (sname,age,phone) values (#{sname},#{age},#{phone})
    

UUID实现主键自增

先来看两条语句,uuid是个函数,用来获取uuid,dual是个虚拟表,用来构成select语法规则。

4.2 JavaEE-MyBatis_第6张图片

 replace替换函数,把uuid里的“-” 替换为“”

4.2 JavaEE-MyBatis_第7张图片

方式1:使用selectKey标签 在sql语句执行前 通过sql获取uuid并存入实体类对象中

StudentMapper.xml


    

    select replace(UUID(),"-","") id from dual

        insert into student (id,name,age)values(#{id},#{name},#{age})
    

测试

  public void test3() throws Exception {
        SqlSessionFactory sqlSessionFactory;
        sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
       Student s= new Student("","张三",18);
        int insert = mapper.insert(s);
        sqlSession.close();

    }

方式2:在java代码中生成uuid封装为实体类直接存入数据库

 StudentMapper.xml


    
        insert into student (id,name,age)values(#{id},#{name},#{age})
    

测试

@Test
    public void test3() throws Exception {
        SqlSessionFactory sqlSessionFactory;
        sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
          String id=UUID.randomUUID().toString().replace("-","");
       Student s= new Student(id,"dbz",18);
        int insert = mapper.insert(s);
        sqlSession.close();

    }

*4.2.2.3.4 resultMap结果映射

resultMap :用于处理复杂的返回结果集

1.为查询结果列与对象不同进行映射

Student类

public class Student{
    private String id;

---省略----
}

mysql中student表 

4.2 JavaEE-MyBatis_第8张图片

select * from student

根据ORM思想创建的相应student类中属性为id,而数据库中表列为sid,试问,是否能正常查询出数据?

结果如下所示

 为什么会这样?

原因就在于,数据库字段与对象属性名不一致。

通常情况下,数据库字段与对象的属性名要一致,但是,如果不用一致就需要进行不同的映射,这时候就需要使用resultMap。

resultMap结果映射

标签

标签 说明
resultMap 处理复杂的返回结果集 (数据库字段与属性名不相同时,就需要映射)
id 标识主键列
result 标识非主键列
colleaction 标识集合属性
association 标识类属性

属性

属性 说明
property 定义java中id的属性名
column 定义数据结果中对应的列名
property 实体类字段(属性)
ofType 集合存储数据类型属性
select 懒加载执行时调用获取数据的方法(也可以调用mapper中的方法,通过namespace.id使用)
fetchType fetchType="lazy":懒加载,默认false
javatype、 jdbctype 、typehandler 自动识别不需要定义

4.2 JavaEE-MyBatis_第9张图片



    
        
        
        
    
    
对比普通的select查询语句





2.一对一查询

先来思考一个问题:什么是一对一查询?

比如,创建一张学生表和学院表,一个学生只归属于一个学院,那么学生表和学院表之间的关系就是一种一对一的关系。

模板

方法一: 

         设置主键列
          

        设置其它的列

         property:类属性的名字

        column:表中列的名字
        

            一对一映射属性也是当前类的属性,需要额外使用association标签配置
         property:类属性的名字
         javaType 一对一映射必须书写该属性指定类属性的类型(自定义类型无法自动识别)
        
            
            
        
    

方法二: 通过设置autoMapping 来自动映射  默认false
    
        
    

Student类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private int sid;
    private String sname;
    private String age;
    private String phone;
    private Department department;  //这里就是一对一的体现,一个student(学生)只能属于一个department(学院)


}

Department类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {
    private int depid;
    private String depname;
    private String principal;

}

 UserMapper.xml

法一:
    

    
         
        
        
        
        
         到Role属性,由于Role是User对应的Role类,所以通过association来映射
        
            
            
            
        
    
    


      
法二:
    
        
    

    

 查询结果

3.一对多查询

对比了一对一,一对多的关系,就好比,学生表和课程表,一个学生对应很多课程,这就是一对多关系。

student类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student{
        private int sid;
        private String sname;
        private ArrayList objList;
}
 
  

Object类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Object{
        private int oid;
        private String oname;

}

StudentMapper.xml 




    
        
        
        
        
                  
                  
        
    
    

注:

和association一样 collection也可设置自动映射

autoMapping="true">

里面不需要再手动写,由mybatis自动映射

autoMapping="true">

 注意:一对多如果开启自动映射 可能导致每条数据单独映射 不会存入集合为一个对象赋值

4.懒加载/延迟加载

什么是懒加载:只显示你当前需要的数据,其他数据等到需要时再动态加载。

场景:比如张三的空间有1000张照片,手机屏幕只能同时展现3张照片,如果把这1000张照片都查询好后展示出现非常浪费内存,并且张三只会看几张照片。懒加载或者延迟加载就是只先查询出三张照片,张三如果继续往后浏览就再查询出三张照片展示。

Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载

在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。

基本原理

使用CGLIB(一种动态代理机制,在《4.3JavaEE-Spring》会讲到)创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,那么就会单独发送事先保存好的查询关联B对象的sql,查询B,再调用a.getB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。

4.2.3 动态SQL标签

 MyBatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。

执行原理是,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql。

4.2.3.1 if 标签

    当uid为0,利用username来查询,当username为null,就全部查询
    

测试

    public void test4() throws Exception {
        SqlSessionFactory sqlSessionFactory;
        sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(Resources.getResourceAsStream("mybatis-config.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        ArrayList users = mapper.select(1,null);
        System.out.println(users);
        sqlSession.close();

4.2.3.2 where标签

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。(可以用于代替where 1=1)


 测试

 ArrayList users = mapper.select(0,"zhang");

4.2.3.3 trim标签

trim 元素来 自定义where 功能

属性

prefix 添加到sql语句前缀的关键字
prefixOverrides 忽略sql语句的前缀  
 suffix 添加到sql语句后缀的关键字
suffixOverrides  忽略sql语句后缀
 

4.2.3.4 choose、when标签

 choose和when搭配可以实现if-else的效果。

    

4.2.3.5 set标签

如下更新语句,问:能否实现单单修改username,而不修改password。

 
       update set username=#{username},password=#{password}
       where uid=#{uid}
 

显然是不能的,即使password什么也不填,也会将password修改为null。

而set标签可以

   
        update user
            
            
                username=#{username} ,
            
            
                password=#{password} ,
            
            
                phone=#{phone} ,
            
            
        where uid=#{uid}
    

 只更新uid=1 的user的name为lisi,其余不变

 mapper.update(new User(1, "lisi", null, null));

4.2.3.6 foreach标签

4.2.3.6.1 属性

item 遍历取出的数据
index 遍历数据的索引
colleaction 遍历的数据集合  

集合:list  

数组:array

open 开始拼接时添加的字符串
separator 分隔符
close 结束后最后添加的字符串

  
 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int [] arr={7,4,6,2};
        ArrayList users = mapper.select(arr);

4.2.3.6.2 批量操作



    
        delete from user where uid in
        
            #{value}
        
    


    
        insert into user (username,password,phone) values
        
            (#{value.username},#{value.password})
        
    

4.2.4 注解开发

使用注解的方式,Dao层接口里书写简单的sql语句,代替mapper.xml文件。

但是注意,复杂的sql语句还是要在mapper.xml文件中书写,注解不适合复杂的sql语句。

 4.2.4.1 @Select()

  @Select("select * from user")
  ArrayList select();

 4.2.4.2 @param()

int selectByNameAndJob(@Param("name")String name,@Param("age")int age);

 4.2.4.3  @Insert()

传入的参数是对象,所以可以用类的属性名(因为属性有get方法)

 @Insert("insert into student(sname,scoll) value(#{sname},#{scoll})")
    int insertStu(Student student);

 4.2.4.4  @Update()

   @Update("update student set sname=#{sname} where sid=#{sid}")
    int updateStu(Student student);

 4.2.4.5  @Delete()

  @Delete("delete from student where sid=#{sid}")
    int deleteStu(int sid);

 4.2.4.6  @Results()、@Result()

@Results(id="唯一标识",value={result标签数组})

@Result(id="true/false是否为主键",column="数据库列",property="属性名")

 4.2.4.7  @resultMap()

面试题:MyBatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?

映射形式有两种情况:

第一种情况:数据库字段名与实体属性名一致。(这种情况下系统会自动完成封装)

第二种情况:数据库字段名与实体属性名不一致。

(1)使用标签 逐一地定义数据库字段名和实体属性名之间的映射关系。

(2)使用 sql 的设置别名功能,将数据库字段名的别名书写为实体属性名。

 4.2.4.8  @One() 一对一查询

 one=@One(select="执行方法",fetchType="加载方式(FetchType.LAZY:懒加载)")

 类似于mapper.xml中的association

数据库表结构

4.2 JavaEE-MyBatis_第10张图片

 user类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int uid;
    private String username;
    private String password;
    private Role role;

}

 userMapper接口


public interface UserMapper {

    @Select("select * from user")
    @Results(id="uid",value = {
            @Result(id = true,column ="uid",property = "uid"),
            @Result(id=false,column = "username",property = "username"),
            @Result(id=false,column = "password",property = "password"),       
//            column代表查询条件对应的值 一般使用主键
            @Result(column = "uid",property = "role",one = @One(
                    select = "mapper.RoleMapper.selectByUid",fetchType = FetchType.LAZY
            ))
    })
    ArrayList select();

}

 role类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
        private int rid;
        private String name;
}

 roleMapper接口

public interface RoleMapper {
    @Select("select r.* from user_role ur,role r where r.rid=ur.rid and uid=#{uid}")
    Role selectByUid(@Param("uid") int uid);
}

由于设置的懒加载,所以数据是动态加载的(sql执行了两次),而不是一次全部加载。 

4.2 JavaEE-MyBatis_第11张图片

 4.2.4.9  @Many() 一对多查询

用法和@one一样

:@Many(select="执行方法",fetchType="加载方式"(FetchType.LAZY :懒加载))

类似于mapper.xml中的colleaction

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
    private int rid;
    private String name;
//一对多,一个role可能对应一个或多个admin
    private ArrayList adlist;
}
  @Select("select * from role")
    @Results(id = "rid" ,value = {
            @Result(id = true,column = "rid" ,property = "rid"),
            @Result(column = "name",property = "name"),
            @Result(column = "rid",property = "plist",many = @Many(
                select = "mapper.PermissionMapper.selectByRid",fetchType = FetchType.LAZY
                ))
    })
    ArrayList select();

4.2.5 深入理解MyBatis

4.2.5.1 MyBatis核心组件

在Mybatis中,SqlSession对数据库的操作,委托给执行器Executor来完成
Mybatis执行过程,主要的执行模块是: SqlSession->Executor->StatementHandler->数据库

 核心组件:

1.动态代理 MapperProxy
2.SQL会话 SqlSession
3.执行器 Executor
4.JDBC处理器 StatementHandler

 4.2.5.1.1 MapperProxy

代理

4.2.5.1.2 SqlSession

SqlSession采用了门面模式方便来让我们使用。他不能跨线程使用,一级缓存生命周期和它一致;
基本功能:增删改查基本的操作功能;
辅助功能:提交、关闭会话;
门面模式:提供一个统一的门面接口API,使得系统更加容易使用。

4.2.5.1.3 Executor

基本功能:改、查、 维护缓存
辅助功能:提交、关闭执行器、 批处理刷新
Executor 主要负责维护一级缓存和二级缓存,并提供事务管理的相关操作,它会将数据库相关操作委托给 StatementHandler完成。

我们先回顾一下JDBC的执行器

JDBC有三种执行器:

Statement(简单执行器):执行静态SQL

PreparedStatement(预处理执行器):设置预编译,防止SQL注入

CallableStatement(存储过程执行器):设置参数、读取参数(用于执行存储过程)

MyBatis的执行器

4.2 JavaEE-MyBatis_第12张图片

BaseExecutor(基础执行器)实现了Executor接口,其中简单执行器是默认的执行器。

CachingExecutor(二级缓存执行器)你开启二级缓存则会实例化它,在 BaseExecutor 的基础上,实现二级缓存功能。 (注意: BaseExecutor 的本地缓存,就是一级缓存)

 BaseExecutor下有三个执行器

  • SimpleExecutor(简单执行器)
  • ResuseExecutor(可重用执行器)
  • BathExecutor(批处理执行器

MyBatis核心源码剖析 ⑧ 核心执行器executor详解 - 知乎

4.2.5.1.4 StatementHandler

经过执行器处理后交给了StatementHandler(声明处理器);
主要作用就是:参数处理、结果处理;
StatementHandler 首先通过  ParameterHandler 完成 SQL 语句的实参绑定,然后通过  java.sql.Statement 对象执行 SQL 语句并得到结果集,最后通过  ResultSetHandler 完成结果集的映射,得到结果对象并返回。

 4.2.5.1 MyBatis与JDBC

1、JDBC数据库连接频繁创建、释放造成系统资源的浪费,影响系统性能。MyBatis中可在SqlMapConfig.xml中配置数据库连接池。

2、传统sql语句和代码书写在一起,由于sql变换较大,导致代码维护不易。MyBatis将sql与代码分离。

3、向sql语句传参数麻烦,sql语句的where条件各不相同,而且必须保证占位符和参数一一对应。MyBatis自动将java对象映射至sql语句。

4、对结果集解析麻烦,且解析前需要对结果集遍历。MyBatis自动将sql执行的结果集映射至java对象。

如;

resultType:就是将结果集映射成指定的对象

4.2.5.2 MyBatis步骤

  1. 创建SqlSessionFactory
  2. 通过SqlSessionFactory创建SqlSession
  3. SqlSession执行数据库操作
  4. session.commit()提交事务
  5. session.close()关闭会话

4.2 JavaEE-MyBatis_第13张图片

4.2.5.3 MyBatis实现分页 

有四种实现分页方式

  1. 查询出所有信息,自定义分页
  2. 使用limit分页
  3. RowBounds分页
  4. pageHelper插件分页

4.2.6.1 

4.2.5.4 MyBatis批处理

4.2.5.4.1 Foreach标签

批量插入


    
        insert into user (username,password,phone) values
        
            (
            #{value.username},
            #{value.password},
            ......
            )
        
    

批量更新

   
        
        UPDATE user
                
            PRC_RESULT = #{value.password},    
            PRC_ERRMSG = #{value.name}   
               
        WHERE id= #{id}     
    

批量删除



    
        delete from user where uid in
        
            #{value}
        
    

4.2.5.4.2 BATCH

要批量执行的话,JDBC连接URL字符串中需要新增一个参数:rewriteBatchedStatements=true

master.jdbc.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&rewriteBatchedStatements=true
 

 MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效
 

publicvoid test() {
// Spring 集成的情况下,事务连接由 Spring 管理,所以可以不手动提交
 //打开批处理
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
     OrderDetailMapper mapper = sqlSession.getMapper(OrderDetailMapper.class);
    for(int i=0;i<=List.size()-1;i++){
        mapper.insert(List.get(i));
    }
}

 添加rewriteBatchedStatements=true这个参数后的执行速度比较:
同个表插入一万条数据时间近似值:
JDBC BATCH 1.1秒左右 > Mybatis BATCH 2.2秒左右 > 拼接SQL 4.5秒左右

4.2.5.5 Mapper接口可以重载吗

在mybatis框架中,写Dao层的Mapper接口时是不可以进行方法的重载的

MyBatis使用package+Mapper+method全限名作为key去调用方法。

假设有方法重载

public interface Mapper{
    public int function(int id);
    public int function(String name);
}
,都会被解析为一个 MapperStatement 对象

Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。

你可能感兴趣的:(JAVA基础知识体系,mybatis,java,mysql,maven)