Mybatis

mycat作用

读写分离
分库分表
工作使用并不会有很大的感受,仅仅是在配置文件中进行配置,然后让其生效。因为只是连接的mycat创建的一个虚拟的客户端什么是mybatis
是基于ORM的持久性框架,封装JDBC。
ORM 面向对象的封装方式
mybatis主要采用的技术是映射,通过映射的方式传递信息由框架进行解析,然后与数据库进行交互最后完成对数据集的持久化操作。预编译
可以优化sql,可以避免在程序编译运行的时候再次进行加载。 解决sql注入和提高查询效率 # $ 的区别
sql注入问题  $引起
# 是会在占位符替换后加上‘ ’   $ 是直接替换 不会添加''mybatis的映射方式
1.resultMap/type
2.查询后的结果定义别名使用时,必要的接口对应条件九种动态sqlhibernate和mybatis的区别(重点)
自动和半自动的区别hibernate由于全自动,所以不是开箱即用。故使用相对复杂  hql语句 通用所有查询语法 完全实现jba接口mybatis是半自动  更加灵活 可以自定义sql语句  sql语法  没有完全实现jba接口

Mybatis的介绍

mybatis是Apache软件基金会下的一个开源项目,前身是iBatis框架。2010年这个项目由apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了github。是持久层框架,用来访问数据库,只是众多持久层框架中一个,也是ORM框架:对象关系映射框架。

优缺点❤️

mybatis的优点1. 简单易学:mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个SQL映射文件即可。
2. 使用灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。
3. 解除SQL与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。mybatis的不足1. 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
2. SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
3. 框架还是比较简陋,功能尚有缺失,二级缓存机制不佳

ORM映射机制(Object Relational Mapping 对应关系映射)

什么是ORM?将关系型数据库映射成面向对象编程的语言,如果使用框架,这由框架去完成。mybatis主要采用的技术是映射,通过映射的方式传递信息由框架进行解析,然后与数据库进行交互最后完成对数据集的持久化操作。

Mybatis的两种映射方式

java与数据库表所对应的关系:      ​       类 -------> 表结构​      对象------>记录​     类成员---->字段名

1.XML配置文件

核心配置文件

核心文件本质上在mybatis的底层会创建出一个configuration的实体类,该配置文件有很多标签,各个标签的位置不可变,必须从上到下排列,可以省略。下列仅列举常用标签顺序。在xml文件中点击某一个标签可以进入到源码中查看顺序

properties

​ 设置外部属性文件(properties),以替代其他位置中的值,${文件中的key}

​ properties标签中也有url属性,连接其他地址的配置文件

如果是用第二种写法,那么在对应的调用出要使用${property标签中的name}

有两种写法,如下

<!--引入数据库连接的配置文件-->
//第一种写法
<properties resource="jdbc.properties"/>
//第二种写法
<properties><!--在文档内定义键和值,文件要放在src否则找不到路径--><property name="jdbc.driver" value="com.mysql.jdbc.Driver"/><property name="jdbc.url" value="jdbc:mysql://localhost:3306/day25"/><property name="jdbc.username" value="root"/><property name="jdbc.password" value="root"/>
</properties>
注意: 当两种方式都使用的时候,以resource的文件内容为主执行流程是先执行内容的property标签再执行resource,所以会进行覆盖。

settings

设置其他文件信息 如log4J的配置信息

<!--配置LOG4J-->
<settings><setting name="logImpl" value="log4j"/>
</settings>

typeAliases

​ 为实体类设置别名,如果不配置该属性则所有类均必须使用全限定类名作为查找依据

​ 有两个属性标签,package和typeAliase 一个是给整个包下的所有类设置别名,一个是为单个类设置别名,可以存在多个子标签

​ package标签中的属性是name

​ typeAlias标签中的属性是type

<!--起别名-->
<typeAliases><package name="com.itheima.bean"/><typeAlias type="com.itheima.bean.Classes"/><typeAlias type="com.itheima.bean.Course"/>
</typeAliases>

​ 在mybatis中已经定义很多别名,我们可以直接使用

  • 内置别名

    别名是小写的,以下类型可以直接使用

parameterType="java.lang.Integer" 换成 parameterType="int"
别名 真实类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator

说明:

  • 内置别名可以直接使用

  • 别名不区分大小写

  • 自定义类的别名就是类名

plugins

​ 额外插件时需要进行配置的标签,如使用插件标签PageHelper的时候

<!--集成分页助手插件-->
<plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

environments

对JDBC连接信息进行设置,指定连接池和事务管理器

  1. transactionManage标签 事务管理器

    type可以指定为

    1. JDBC(使用JDBC的事务)
    2. Managed(使用Spring,web容器)
  2. dataSource标签 数据源信息 (连接池)

​ type可以指定为

  1. Pooled 使用

  2. Unpooled 不使用

  3. JNDI 用名字获取web中资源

    3.可以有多个environment标签来指定多个数据库信息

​ 但是最终使用的数据库以environments标签中default=“id名”的哪一个为准

<!--environments配置数据库环境,环境可以有多个。default属性指定使用的是哪个-->
<environments default="mysql"><!--environment配置数据库环境  id属性唯一标识--><environment id="mysql"><!-- transactionManager事务管理。  type属性,采用JDBC默认的事务--><transactionManager type="JDBC"></transactionManager><!-- dataSource数据源信息--><dataSource type="POOLED"><!-- property获取数据库连接的配置信息 --><property name="driver" value="${driver}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></dataSource></environment>
</environments>

mappers

有两种方式进行加载映射配置文件,在此标签下可以对多个包或者文件进行加载

    <!-- mappers引入映射配置文件 --><mappers><!--子元素:mapper属性:resource:加载类路径下指定的配置文件,注:分隔符是/,而不是点号url: 读取指定路径下配置文件,或者网络的配置文件<mapper url="file:///d:/UserMapper.xml"/>class: 指定接口的完全限定名,用于注解的配置,不需要XML文件。<mapper class="com.itheima.dao.UserMapper"/>子元素:package1. 指定扫描哪个包下所有的DAO接口,如果使用这种写法,接口名与配置文件名字要相同。如:接口名UserMapper.java 配置文件名:UserMapper.xml2. 接口与配置文件必须放在同一个包下--><!-- mapper 引入指定的映射配置文件   resource属性指定映射配置文件的名称(单个) --><mapper resource="StudentMapper.xml"/>  //放在src下时,所指定的xml配置文件<mapper class="com.itheima.dao.UserMapper"/> //指定接口,用.隔开。注解时直接用;配置文件时,配置文件要在该接口文件下 <package name="com.itheima.dao"/> //包名的全限定名,采用配置文件时,配置文件名字与接口名要相同</mappers>

2.映射配置文件

明确:

全限定名
根标签mapper的设置 name命名空间必须为对应的接口的
动态代理技术
mybatis的底层是使用的动态代理技术,所以才可以通过我们指定的接口,找到对应的方法进行执行再返回数据给调用处。在使用mybatis框架的时候,主要是对接口和映射配置文件进行操作。
事务的提交
在mybatis中,默认是手动提交的,对增删改进行操作的时候,如果不开程序没有错误的时候就不会报任何错误。也不会出现任何提示。数据库不会有任何更改。说明:如果在同一个方法中,有多个DML数据库操作,需要使用手动提交的方式。在mybatis的核心类工厂中提供了重量级

自动提交:

SqlSession sqlSession = sqlSessionFactory.openSession(true);

手动提交需要自己进行提交或者回滚操作,运用场景与转账一致:

sqlSession.commit();
sqlSession.rollback();
占位符

写SQL语句的时候,由于JDBC使用的是preparedStatement,所以在指定参数的时候必定有占位符,mybatis提供的占位符为

#{名字随意但要有意义},但是如果传递进来的参数为实体类时,那么#{}中必须填实体类对象的成员变量名

当sql进行解析后自带‘ ’ ,不需要再写‘ ’,如:

UPDATE student SET name = #{name},age = #{age} WHERE id = #{id} ;

在sql中的写法:UPDATE student SET name = ‘ 张三 ’,age = ‘ 23 ’ WHERE id = 2;

还提供${value}进行解析后不带‘ ’ ,此方法有SQL注入问题且变量名必为value。

映射文件的三种输入类型
主要针对parameType的填写1. 简单类  八大基本类型具体书写格式可参照typeAliases的别名规范2. POJO类(javaBean)- 实体类自己写的单一类 例如User,Student等,主要用于单表查询当传入的是实体类的时候,必使用实体类的名字而不是随意乱写- 实体类中含实体类自己写的类grade中含有多个其他类作为成员变量存在 如:user ,string ,int此时若传入类型resultType=grade ,那么在sql语句中应该用user.user的成员变量来进行调用如:select * from student where name  like '%' #{user.name} ''%'
映射文件的两种输出类型
 主要针对resultType的填写1. 简单类   八大基本类型
2. 实体类    在接口处使用集合来进行封装的时候 ,配置文件中填写泛型的类型,底层自动封装为集合返回

事务特性❤️

1.原子性 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。
2.一致性 事务开始前和结束后,数据库的完整性约束没有被破坏 。
3.持久性 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
4.隔离性 同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰

小结

  1. 接口与xml映射配置文件要放在同一包下,接口名与配置文件名要一致

    满足这个条件后,mappers中才可以class,resource属性,package标签通用,使用注解方式优先使用package标签扫描整个包

  2. 配置文件的命名空间要使用接口的全限定类名

  3. 增删改时要提交事务 opensession(true)。

  4. 传入的参数为POJO的第二种形态时要使用对象。成员变量为sql的占位符进行赋值

  5. 接口的参数和配置文件中定义规则

    接口中的名字=配置标签中的id

    接口中的形参=parameterType

    接口中的返回值=resultType/resultMap

    接口中的方法名不可有重载方法

  6. 核心配置文件的顺序必须由上到下依次排列(不用的省略掉)

Mybatis开发

增删改

增删改的标签是可以进行通用的,为何可以通用而不会报错?因为底层逻辑是增删改都使用了同一套执行流程,对传进来的语句和占位符进行赋值,然后直接返回。(具体在JDBC文档中查看对mybatis底层的框架模板)

增删改查的主要标签的各个含义

  • id 该标签的名字,通过该id名查找或者调用
  • resultType/resultMap 执行语句之后返回的结果集
  • parameterType 调用此方法时前方传来的数据类型 可以省略

    • 提供子标签 selectKey

      作用是在插入一条语句的同时,可以获取一个该对象插入之后的主键值。

      • 注解

        /*** 添加用户* @SelectKey 获取新添加的记录主键值* 属性:*  keyColumn:表中主键列名*  keyProperty:实体类中主键的属性名*  resultType: 主键返回的数据类型*  before: true表示在insert语句前执行,false表示在insert语句后执行*  statement: 要执行的SQL语句*/
        @Insert("insert into user values(null, #{username},#{birthday},#{sex},#{address})")
        @SelectKey(statement = "select last_insert_id()" ,keyColumn = "id", keyProperty = "id", resultType = Integer.class, before = false)
        int insertUser(User user);
        
        • xml

          两种方式

    1. 子标签selectKey
      2. 在insert标签中 调用属性useGeneratedKeys=“true” (oracle不支持)

      其他标签不支持该功能,因为其他标签是可以通过查询来得到对应的主键值的。

      注意:

      1. 在插入语句之后就要马上执行该子标签

      2. 子标签的各个参数含义

        selectKey :获取新加的主键值
        keyColumn: 表中主键列名
        keyProperty:实体类中主键属性名
        resultType: 主键的数据类型,int类型
        order:在insert执行前,还是执行后执行这个查询
        标签体:要执行的查询语句

        3.通过对象的getId()得到新增的主键值

    <insert id="insert" parameterType="student">INSERT INTO student VALUES (null,#{name},#{age})<selectKey keyColumn="id" resultType="int" keyProperty="id" order="AFTER" >select last_insert_id();  //这个方法是数据集的聚合函数方法 </selectKey>
    </insert>
    
    <!--
    parameterType可以省略
    如果参数是实体类,花括号中是属性名:#{属性名}
    keyProperty:实体类中主键属性名
    keyColumn:表中主键列名
    useGeneratedKeys: 使用生成的主键
    使用这种方式的前提是:数据库本身有主键自动增长的功能,如:mysql, sql server。注:Oracle没有
    -->
    <insert id="addUser" parameterType="user" keyProperty="id" keyColumn="id" useGeneratedKeys="true">insert into user values (null,#{username},#{birthday},#{sex},#{address})
    </insert>
    
    System.out.println(student.getId());
    
  • select

使用配置文件查询

注:

​ 1.自动封装类名与字段名

​ resultType是默认自动封装,如果名字不符无法封装。

​ 在resultMap中,设置标签autoMapping=true 自动封装,如果名字不符无法封装。

​ 当类名是驼峰命名,表名是标准命名,可以开启核心配置文件中的mapUnderscoreToCamelCase

​ 如: 类中变量 userName 字段名 user_name (sql中的命名规范)就可以在不指定名字的情况下进行自动映射

    <settings><!--映射下划线为驼峰命名法,会自动将create_time对应createTime--><setting name="mapUnderscoreToCamelCase" value="true"/><!--log4j--><setting name="logImpl" value="log4j"/><!--开启延迟加载--><setting name="lazyLoadingEnabled" value="true"/></settings>

​ 2.使用resultMap的时候,必定有一个类中存在需要查询的所有字段或者间接拥有(实例对象/集合<实例对象>),否则无法达到理想查 询目标,如果查询类中存在对应字段,但是没有使用resultMap映射,则输出时对应成员变量为null。

​ 3.resultMap进行封装的时候,实际上是通过对应的表查询出来的id来封装的

​ 1.使用一个resultMap进行整合,主类中有多个成员变量,则有多个collection或association标签

​ 若autoMapping=“true”,相同字段名同样需要一一映射(不建议开,影响可读性)

使用到的关键属性名 ofType 指定property确定的成员变量的数据类型

//核心配置文件
<mappers><mapper resource="com/itheima/one_to_many/OneToMany.xml"/>
</mappers>
//接口
public interface OneToManyMapper {//查询全部public abstract List<Classes> selectAll();
}
<mapper namespace="com.itheima.table01.OneToOneMapper"><!--id:表示接口中方法名resultType:表示方法返回的数据类型,如果返回的是集合,这里指定集合中每个元素类型select标签标签体就是SQL语句--><resultMap id="selectaaaaaa" type="Card" autoMapping="true"><id property="id" column="cid"/><!--开了autoMapping="true"就会自动映射相同名字的--><!--<result property="number" column="number"/>-->//下面是card类中的 实例对象 Person p<association property="p" javaType="Person" autoMapping="true"><id property="id" column="pid"/><!--<result property="name" column="name"/>--><!--<result property="age" column="age"/>--></association>//下面是card类中的 集合对象 list<course> courses<collection property="courses" ofType="Course"><id property="id" column="cid"/><result property="name" column="cname" /></collection></resultMap><select id="selectAll" resultMap="selectaaaaaa" >SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id</select>
</mapper>
  1. 使用配置文件自关联
指定DeptInfo的对应映射关系
<resultMap id="BaseResultMap" type="DeptInfo"><id column="dept_id" jdbcType="VARCHAR" property="deptId"/><result column="dept_name" jdbcType="VARCHAR" property="deptName"/><result column="parent_id" jdbcType="VARCHAR" property="parentId"/><result column="state" jdbcType="DECIMAL" property="state"/><!--自关联属性--> parent是一个实体类DeptInfo Column是指定自关联的字段名调用对应的查询方法,就会作为传递值传递到findById中当作id<association property="parent" column="parent_id" javaType="DeptInfo" select="findById" ></association>
</resultMap>

​ 3…使用虚拟表resultMap进行映射

 虚拟表指向主表,标签体指向从表(此处只有一个从表,如有多个就分别指向每一个从表)

使用到的关键属性名 extends 虚拟表是继承哪一个主表

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:命名空间,指定接口的完全限定名字-->
<mapper namespace="com.itheima.table02.OneToManyMapper"><!--id:表示接口中方法名resultType:表示方法返回的数据类型,如果返回的是集合,这里指定集合中每个元素类型select标签标签体就是SQL语句--><!--映射class主表--><resultMap id="c" type="Classes"><id property="id" column="cid"/><result property="name" column="cname"/></resultMap><!--映射student从表--><resultMap id="s" type="student" ><id property="id" column="sid"/><result property="name" column="sname"/><result property="age" column="sage"/></resultMap><!--映射两者的虚拟表--><resultMap id="cs" type="Classes" extends="c"><collection property="students" resultMap="s"/></resultMap><select id="selectAll" resultMap="cs">SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid</select>
</mapper>

使用注解查询

没有映射配置文件,仅在核心配置文件mappers标签中指定对应接口即可。

<mappers><mapper class="com.heima.dao.StudentMapper"/>   可扫描单个接口<package name="com.heima.dao"/>          按包扫描
</mappers>
  • 单表查询

    可以为@Results 取名字,以复用 使用 id=

    value 参数是一个@Result数组,每个@Result对象反应一个列的映射

    如果只有一个value属性值,value的名字可以省略

  • /*** DAO接口*/
    public interface UserMapper {/*** 通过id查询用户的方法*/@Select("select * from user where id=#{id}")User findUser(int id);/*** 查询user2的表,其中user_name是对不上属性名的username* @Select 注解用于查询* @Results 注解用于映射不同的列和属性名*  属性:id用来定义这个映射的名字,供其它查询语句重用*  属性:value 参数是一个@Result数组,每个@Result对象一个列的映射* @Result 用来映射每一列*  属性:column 指定列名*  属性:property 指定属性名*/@Select("select * from user2 where id=#{id}")@Results(id = "userMap",value = { @Result(column = "user_name",property = "username") })User findUser2(int id);/*** 查询user2表中所有的记录* @ResultMap 用来引用上面定义的映射*/@Select("select * from user2")@ResultMap("userMap")List<User> findAllUsers2();
    }
    
public interface StudentDao {@Select("select * from student")List<Student> selectAll();@Delete("delete from student where id=#{id}")int delete(Integer id);@insert("插入语句")int a(Student stu);@update("更新语句")int up(Student stu);
}
  • 多表查询

    注意:

    1. @Results({})是一个数组,所以中间每个result要用,隔开
    2. javaType = Person.class 可以省略
    3. 主表成员变量与表的字段同名可以不指定,否则必须指定
    4. 主表的查询语句可以是单表查询也可以是表连接查询,只要最终与预期查询的表结果一致即可
    5. one/many的取值是根据 主类中的成员变量是集合还是实体类
    6. 注解中的主类不需要指定,默认以返回值(泛型/实体类)为主类
    7. 传递的方法不局限于同一个接口中,可以指定对应接口的全限定名.其中的方法调用也行
    public interface CardMapper {@Select("select * from card ")
    @Results({@Result(property = "id",column = "id"),//同名可以不指定@Result(property = "number",column = "number"),//同名可以不指定@Result(property = "p",      //主类中的变量名   主要目的是为成员变量P进行封装column = "pid",   //往下传递的参数javaType = Person.class,  //主类的数据类型one=@One(select = "selectById"))  //参数所要传递到的方法处// many=@Many(select = "com.heima.mapper.StudentMapper.selectById")) 调用其他接口中的方法...   // 如果有多个方法需要调用,则继续@Result})
    public List<Card> selectAll();  //默认以返回值的类型进行封装@Select("select * from person where id=#{id}")  //除主表外,其余表都是单表查询以查询对应字段public Person selectById(Integer id);}
    

延时加载

什么是延迟加载,为什么要使用延迟加载?概念:如果一张表关联了另一张表的数据,只加载这张表中的数据,它关联的另一张表中的数据等到需要用的时候才去加载,称为延迟加载,也叫懒加载。在使用表关联查询的时候,是一次性把所有关联的多张表的数据全部查询出来,并且封装到对象中。如:1对1或1对多。多表关联查询缺点:1. 查询速度更慢2. 更加占用对象的资源,还可能造成资源的浪费。
延迟加载使用指南
  1. 映射配置文件中使用,必须使用第二种虚拟表的方式进行映射

前提:一个类中含有多个成员变量且指向不同的类。如果不使用延迟加载则在查询的时候会自动全部查询出来,而不是按需查询

一对一执行流程

一对多执行流程

  1. 注解中使用

    首先要开启延迟加载

/**通过id查找用户@Result 用来映射一个列和属性名属性:property 指定另一方在实体类中属性名属性:column 指定为当前表中主键属性:one 表示1对1的关联@One注解:属性:select 指定下一条要执行的SQL语句方法名属性:fetchType 指定是否使用延迟加载 FetchType.LAZY 表示延迟加载  FetchType.EAGER 表示即时加载*/
@Select("select * from user where id=#{id}")
@Results({@Result(property = "userInfo", column = "id", one=@One(select = "findUserInfoById", fetchType = FetchType.LAZY))
})
User findUserById(int id);/**通过id查询用户扩展信息*/
@Select("select * from user_info where id=#{id}")
UserInfo findserInfoById(int id);
开启延迟加载
<settings>
<!--映射下划线为驼峰命名法,会自动将create_time对应createTime-->
<setting name="mapUnderscoreToCamelCase" value="true"/><!--开启延迟加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>

Mybatis核心类的使用及其执行流程

mybatis的三个核心类
  1. SqlSessionFactoryBuilder 召集工人 build()

  2. SqlSessionFactory(传入输入流) 开始造厂 重量级,极其消耗资源,建议设为静态成员只加载一次 openSession(true)

  3. SqlSession 开始营业

    需要在使用前读入核心配置文件

    读入核心配置文件的三种方式

//第1种,mybatis提供
InputStream resourceAsStream = Resources.getResourceAsStream("MybatisConfig.xml");
//第2种
InputStream resourceAsStream = 类名.Class.getResourceAsStream("MybatisConfig.xml");
//第3种
InputStream resourceAsStream = 对象名.getClass().getResourceAsStream("MybatisConfig.xml");
//执行mybatis框架对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
SqlSession sqlSession = build.openSession(true);
XXXDao mapper = sqlSession.getMapper(XXXDao.class);xxx a = mapper.xxx();
//关闭资源sqlSession.close();
mybatis的执行流程

使用框架核心类获取mapper对象—> 读取核心配置文件—>找到映射配置文件

​ —>mapper对象调用方法—>接口中的对应方法—>配置文件对应方法—>连接数据库进行增删改查—>返回数据

​ —>配置文件 —>接口中对应的方法 —>mapper调用处

重点
  • 定义接口中的方法,每个方法都必须是唯一的不可重载 返回值类型,传递参数,方法名

  • 映射配置文件中按增删改查功能的不同定义标签,标签中按各个属性,定义接口中的 返回值类型,传递参数,方法名

    1. 只有查询时,在resultType该属性中才存在返回一个POJO(javaBean[实体类,实体类中的实体类]),其余情况都是int类型
    2. 接口方法中定义的返回类型是List集合,那么配置文件中不可写list,应该写泛型类型。
    3. 在传值入SQL语句中的值#{}的赋值标准:基本类可以随便取名;实体类必须是实体类的成员变量名;实体类中的实体类则要用实体类的成员变量名.实体类中实体类的成员变量名

注:

​ 1.由于每次使用mybatis的时候都需要创建mapper对象,可以提取出来作为工具类进行调用。

​ 2.Mybatis会在底层通过接口的全限定名去调用底层方法selectList/One 方法执行对应的每一个方法,所以需要在配置文件中写接口的全限定名

SQL构建器(了解)

使用注解方式时,Myabatis提供一些额外的方式对注解中的SQL语句进行整合

注意:

​ 1.类中必须使用构造代码块进行调用

​ 2.该方法返回值需要使用String接收,同理在SQL()之后要toString 转为字符串格式

​ 3.注解中使用的是XXXProvider(type= XXX.class, method=“对应被调用方法名”)

package com.heima.bean;import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.jdbc.SQL;public class SqlConstracture {public  String select(){return   new SQL() {{SELECT("*");FROM("student");}}.toString();}public static String delete(){return new SQL(){{DELETE_FROM("STUDENT");WHERE("id=#{id}");}}.toString();}
}
public interface StudentDao {//    @Select("select * from student")@SelectProvider(type =SqlConstracture.class,method ="select" )List<Student> selectAll();//    @Delete("delete from student where id=#{id}")@DeleteProvider(type = SqlConstracture.class,method ="delete" )int delete(@Param("id") Integer id);}

小结

  • 当类成员变量与字段名相同时,注解不需要指定,配置文件需要指定

如遇到驼峰命名和字段名_重合时,可以在settings里开启核心配置文件中的mapUnderscoreToCamelCase 来自动映射

  • 接口的方法不可以重载(方法唯一)

动态SQL

在需要使用动态SQL的时候,都在配置文件中执行,如果使用注解的方式写动态SQL没有可读性

提供动态SQL的标签有

if,where,set,foreach,(sql,include),(trim),(choose,otherwise,when)

不是独立关系,可以进行相互匹配只要逻辑上行得通即可,使用了这个标签就不需要再写对应位置上的关键字

if和where标签

if标签的作用: 对传递进来的数据进行逻辑判断,如果符合条件就进行拼接

where标签的作用:在where标签中进行多个if标签的判断的时候,可以自动去除在语句中存在的多余的and or where关键字

​ 需要注意的是只能自动去除,不会自动添加,少了关键字依然报错

​ 为了增强代码的健壮性,所有if判断的前面都添加对应关键字

<select id="selectCondition" resultType="student" parameterType="student">select * from student<where><if test="id != null">and id = #{id}</if><if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></where>
</select>
set标签

set标签的作用:主要用于update标签,来设置set关键字,以去除更新多个语句时多余存在的 ,

为了增强代码的健壮性,所有if判断的前面都添加

<!--
1. 如果username属性不为空,则更新这个字段。
2. 如果sex不为空则更新这个字段
-->
<update id="updateUser">update user<set><if test="username!=null and username!=''">username=#{username},</if><if test="sex!=null and sex!=''">sex=#{sex},</if></set>where id=#{id}
</update>
foreach标签

foreach标签的作用:对传入进来的参数进行遍历循环再输出到指定的位置上

foreach标签的属性 作用:用于遍历集合或数组,多次拼接生成SQL语句
collection 取值:如果是集合使用list,如果是数组使用array
item 代表集合中每个元素,给元素定义一个名字
separator 每次遍历完成以后添加1个分隔符
#{变量名.属性} 标签体中引用的时候,必须使用变量名.属性名

总共有三种遍历对象: 集合,数组,要遍历集合或者数组在某个resultMap或者JavaBea中

  1. parameterType为集合

    <!--使用foreach标签遍历一个集合,生成SQL语句-->
    <insert id="addUsers" >insert into user (username, birthday, sex, address) values<!--foreach标签的属性:collection 指定是集合还是数组,如果是集合使用list,如果是数组使用arrayitem 表示集合中每个元素的变量名,这里是一个实体类对象separator 每次拼接完以后,添加1个符号分隔--><foreach collection="list" item="user" separator=",">(#{user.username},#{user.birthday},#{user.sex},#{user.address})</foreach>
    </insert>
    
  2. parameterType为数组

    <!--批量删除用户 -->
    <delete id="deleteUsers">delete from user where id in<!--collection: 表示要遍历的集合类型,数组使用arrayitem: 表示其中每个元素的变量名open:表示循环开始前添加一个符号,只会执行1次separator 每次拼接完以后,添加1个符号分隔,执行多次close:表示循环结果以后添加一个符号,只会执行1次--><foreach collection="array" open="(" item="id" separator="," close=")">#{id}</foreach>
    </delete>
    
  3. parameterType为JavaBean ,成员变量为数组/集合

    <select id="findUserInIds" resultMap="userMap" parameterType="queryvo"><include refid="defaultUser"></include><where><if test="ids != null and ids.size()>0"><foreach collection="ids" open="and id in (" item="uid" separator="," close=")">#{uid}</foreach></if></where>
    </select>
    

注意: 当传入的集合或者数组里面存放的是一个一个的实体类的时候,在调用时必须使用#{实体类。成员变量}赋值

sql和include标签

sql标签的作用:提取重复代码到此标签中,

include标签的作用:在对应处通过refid调用sql标签id名

优缺点:在使用的时候会大大简化代码的编写,但是极其影响代码可读性,慎用。

<!--
1. sql标签定义查询条件代码块Map = {minDate='1999-9-9', maxDate='2000-1-1'}在XML中<或>符号是有特殊含义的,通常不能直接写在XML中有两种解决方案:1) 使用转义:&lt; &gt;2) 使用CDATA包裹起来的内容表示纯文本内容,XML解析器不对它进行解析-->
<sql id="sqlCondition"><where><if test="minDate!=null and minDate!=''">birthday >= #{minDate}</if><if test="maxDate!=null and maxDate!=''"><![CDATA[and birthday <= #{maxDate}]]></if></where>
</sql><!--
include标签的作用:引用使用sql标签定义的代码段属性:refid对应上面sql的id值
-->
<select id="findUsersByMap" resultType="user">select * from user  <include refid="sqlCondition"/>
</select><select id="findCountByMap" resultType="int">select count(*) from user <include refid="sqlCondition"/>
</select>

分页插件

PageHelper 可以自动在底层实现分页的效果,是一个三方jar包

分页原理:

package com.itheima.entity;import java.util.List;/*** 封装页面所有属性的实体对象* 使用泛型以后可以通用*/
public class PageBean<T> {//第1组:可以从数据库中查询到的private List<T> data;  //这一页的数据private int count;   //一共有多少条记录//第2组:由用户在浏览器端提供的数据private int current;  //当前第几页private int size;  //每页大小//第3组:由其它的属性计算出来,写在get方法中private int first;   //第一页private int previous;  //上一页private int next;   //下一页private int total;   //总页数=最后一页public List<T> getData() {return data;}public void setData(List<T> data) {this.data = data;}public int getCount() {return count;}public void setCount(int count) {this.count = count;}public int getCurrent() {return current;}public void setCurrent(int current) {this.current = current;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}//计算第1页public int getFirst() {return 1;  //直接返回1}public void setFirst(int first) {this.first = first;}//计算上一页public int getPrevious() {//如果当前页大于1,上一页=当前页-1,否则返回1if (getCurrent() > 1) {return getCurrent() - 1;}else {return 1;}}public void setPrevious(int previous) {this.previous = previous;}//计算下一页public int getNext() {//如果当前页小于最后一页,下一页=当前页+1,否则返回最后一页if (getCurrent() < getTotal()) {return getCurrent() + 1;}else {return getTotal();}}public void setNext(int next) {this.next = next;}//计算总页数 = 总记录数 / 页面大小public int getTotal() {//如果能整除就正好是这么多页,不能整除就加1if (getCount() % getSize() == 0) {return getCount() / getSize();}else {//在java中整数相除得到整数return getCount() / getSize() + 1;}}public void setTotal(int total) {this.total = total;}//注:因为有四个属性是在get方法中计算的,如果直接输出属性值,不一定准确,应该输出get方法才能看到正确的结果@Overridepublic String toString() {return "PageBean{" +"data=" + getData() +", count=" + getCount() +", current=" + getCurrent() +", size=" + getSize() +", first=" + getFirst() +", previous=" + getPrevious() +", next=" + getNext() +", total=" + getTotal() +'}';}
}

主要针对所有的数据库进行分页

步骤:

  1. 导包

  2. 在核心配置文件中设置plugins

    <plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>
    
  3. 调用 PageHelper相关方法

    PageHelper.startPage(第几页,显示的个数); 分页显示,循环显示时仅显示指定个数,使用的位置要放在查询之前

    PageInfo info = new PageInfo<>(调用方法的结果集); 用于调用显示上一页等操作

    package com.itheima.paging;import com.github.pagehelper.PageHelper;
    import com.github.pagehelper.PageInfo;
    import com.itheima.bean.Student;
    import com.itheima.mapper.StudentMapper;
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    import org.junit.Test;import java.io.InputStream;
    import java.util.List;public class Test01 {@Testpublic void selectPaging() throws Exception{//1.加载核心配置文件InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");//2.获取SqlSession工厂对象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);//3.通过工厂对象获取SqlSession对象SqlSession sqlSession = sqlSessionFactory.openSession(true);//4.获取StudentMapper接口的实现类对象StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);//通过分页助手来实现分页功能// 第一页:显示3条数据//PageHelper.startPage(1,3);// 第二页:显示3条数据//PageHelper.startPage(2,3);// 第三页:显示3条数据PageHelper.startPage(3,3);//5.调用实现类的方法,接收结果List<Student> list = mapper.selectAll();//6.处理结果for (Student student : list) {System.out.println(student);}//获取分页相关参数PageInfo<Student> info = new PageInfo<>(list);System.out.println("总条数:" + info.getTotal());System.out.println("总页数:" + info.getPages());System.out.println("当前页:" + info.getPageNum());System.out.println("每页显示条数:" + info.getPageSize());System.out.println("上一页:" + info.getPrePage());System.out.println("下一页:" + info.getNextPage());System.out.println("是否是第一页:" + info.isIsFirstPage());System.out.println("是否是最后一页:" + info.isIsLastPage());//7.释放资源sqlSession.close();is.close();}
    }
    

Mybatis底层原理分析

此处去除所有安全判断之后,经过JDBC的底层,以注解为例分析可得:

这是JDBC底层调用反射框架时的参数

  @Testpublic void queryForObject() {//查询一条记录并封装自定义对象的测试String sql = "SELECT * FROM student WHERE sid=?";Student stu = template.queryForObject(sql,new BeanHandler<>(Student.class),1);System.out.println(stu);}

由此可得:需要用到SQL语句,对应接口的字节码对象,传入的形参。

  1. 当创建mapper实体对象的时候要读入核心配置文件,此时可以得到mappers标签中通过指定的配置文件可以得到所有接口的字节码对象(映射配置文件中有接口的全限定名)
  2. 接口的方法处可以获取到SQL语句,传入的形参和返回值对象(查看使用多对象list还是单对象实例或聚合函数)
  3. 通过验证使用selectList()/selectOne()在底层判断后,调用对应的单,多,聚合的反射方法。传入上述参数。
  4. 到达核心框架处,经过反射和元数据的判断后,执行SQL语句,把结果集对应封装进返回值对象(可以理解为使用了BeanUtils的populate方法),返回调用处
  5. 一一返回,到达调用处,结果框架调用。

其他零散知识点

​ 1.Data类中提供方法 valueOf(日期字符串) 可以将日期格式的字符串转为对应的Data类 以对应数据库中的Data属性

new User(null,“白龙马”,Date.valueOf(“2019-05-01”),“男”,“东海龙宫”);

2.在XML中<或>符号是有特殊含义的,通常不能直接写在XML中

 有两种解决方案:
1) 使用转义:&lt; &gt;
2) 使用CDATA包裹起来的内容表示纯文本内容,XML解析器不对它进行解析<![CDATA[and birthday <= #{maxDate}]]>

3.使用注解的方式,如果方法中的形参不使用map集合进行传递,那么多于一个形参时,使用@Param关键字

如下:

@Select("select * from student where id=#{id} and name=#{name}")
List<Student> selectAll(@Param("id") Integer id,@Param("name") String name);

总结

  1. 核心配置文件的标签必须从上到下按顺序定义,否则报错
  2. 想要注解和配置文件mappers的子标签通用,必须接口与映射文件同名且在同一包下 class属性用.隔开,resource用/隔开
  3. 事务自动提交需要自己手动设置
  4. 核心配置文件中 resultMap有两种格式,单个map更为简单,虚拟表形式主要用于延迟加载时使用
  5. 对#{}的赋值,基本类名字随意,实体类是变量名,实体类中的实体类是实体类变量名.其成员变量名
    tis底层原理分析

此处去除所有安全判断之后,经过JDBC的底层,以注解为例分析可得:

这是JDBC底层调用反射框架时的参数

  @Testpublic void queryForObject() {//查询一条记录并封装自定义对象的测试String sql = "SELECT * FROM student WHERE sid=?";Student stu = template.queryForObject(sql,new BeanHandler<>(Student.class),1);System.out.println(stu);}

由此可得:需要用到SQL语句,对应接口的字节码对象,传入的形参。

  1. 当创建mapper实体对象的时候要读入核心配置文件,此时可以得到mappers标签中通过指定的配置文件可以得到所有接口的字节码对象(映射配置文件中有接口的全限定名)
  2. 接口的方法处可以获取到SQL语句,传入的形参和返回值对象(查看使用多对象list还是单对象实例或聚合函数)
  3. 通过验证使用selectList()/selectOne()在底层判断后,调用对应的单,多,聚合的反射方法。传入上述参数。
  4. 到达核心框架处,经过反射和元数据的判断后,执行SQL语句,把结果集对应封装进返回值对象(可以理解为使用了BeanUtils的populate方法),返回调用处
  5. 一一返回,到达调用处,结果框架调用。

其他零散知识点

​ 1.Data类中提供方法 valueOf(日期字符串) 可以将日期格式的字符串转为对应的Data类 以对应数据库中的Data属性

new User(null,“白龙马”,Date.valueOf(“2019-05-01”),“男”,“东海龙宫”);

2.在XML中<或>符号是有特殊含义的,通常不能直接写在XML中

 有两种解决方案:
1) 使用转义:&lt; &gt;
2) 使用CDATA包裹起来的内容表示纯文本内容,XML解析器不对它进行解析<![CDATA[and birthday <= #{maxDate}]]>

3.使用注解的方式,如果方法中的形参不使用map集合进行传递,那么多于一个形参时,使用@Param关键字

如下:

@Select("select * from student where id=#{id} and name=#{name}")
List<Student> selectAll(@Param("id") Integer id,@Param("name") String name);

总结

  1. 核心配置文件的标签必须从上到下按顺序定义,否则报错
  2. 想要注解和配置文件mappers的子标签通用,必须接口与映射文件同名且在同一包下 class属性用.隔开,resource用/隔开
  3. 事务自动提交需要自己手动设置
  4. 核心配置文件中 resultMap有两种格式,单个map更为简单,虚拟表形式主要用于延迟加载时使用
  5. 对#{}的赋值,基本类名字随意,实体类是变量名,实体类中的实体类是实体类变量名.其成员变量名
  6. 用到动态SQL的时候都要用配置文件的形式,便于可读性

Mybatis使用及原理相关推荐

  1. mybatis的工作原理

    MyBatis 的工作原理 在学习 MyBatis 程序之前,读者需要了解一下 MyBatis 工作原理,以便于理解程序.MyBatis 的工作原理如图 2 所示. 下面对图 2 中的每步流程进行说明 ...

  2. Mybatis简介与原理

    转载自  Mybatis简介与原理 什么是Mybatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到 ...

  3. MyBatis中的原理

    01Mybatis的运行原理 运行过程中涉及到的类 1.1 Resources MyBatis中IO流的工具类 1.1 加载配置文件 1.2 SqlSessionFactoryBuilder() 构建 ...

  4. MyBatis整合Spring原理分析

    目录 MyBatis整合Spring原理分析 MapperScan的秘密 简单总结 假如不结合Spring框架,我们使用MyBatis时的一个典型使用方式如下: public class UserDa ...

  5. 事务-2 Spring与Mybatis事务实现原理

    背景: 本文承接事务-1 事务隔离级别和Spring事务传播机制,是事务专题的第二篇:主题内容是Mybatis和Spring事务原理,结合源码和案例进行介绍. 本文主题内容为事务原理, 尤重Sprin ...

  6. MyBatis动态代理原理

    1. MyBatis核心组件 在详细探究MyBatis中动态代理机制之前,先来补充一下基础知识,认识一下MyBatis的核心组件. SqlSessionFactoryBuilder(构造器): 它可以 ...

  7. MyBatis基本工作原理介绍

    1.MyBatis基本工作原理介绍 计算机的基本工作就是存储和计算,而MyBatis是存储领域的利器.MyBatis的基本工作原理就是:先封装SQL,接着调用JDBC操作数据库,最后把数据库返回的表结 ...

  8. Mybatis 的工作原理及流程

    1.介绍 MyBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用步骤一脉相承,MyBatis的核心对象(SqlSession,Executor)与JDBC ...

  9. Mybatis Dynamic SQL原理——更优雅的使用Mybatis

    Mybatis Dynamic SQL原理 一.Mybatis Dynamic Sql是什么 二.Mybatis Dynamic Sql原理 2.1 mybatis的本质是什么 2.2 mapper接 ...

  10. 12.mybatis核心架构原理

    课程标题<2022超详细mybatis框架源码解读> 课程内容: 1.mybatis环境搭建 2.mybatis使用哪些设计模式 3.mybatis大致运行原理 4.mybatis建造者模 ...

最新文章

  1. pycharm与python环境配置
  2. RabbitMQ消息幂等性问题
  3. 【人工智能】AI如何把招人效率提高四成
  4. mysql 半同步 5.6及5.7
  5. 细说WCF中的会话模式
  6. 假如有Thread1、Thread2、ThreaD3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?
  7. php根据下标倒序排,PHP的三种排序方式
  8. Java设计模式(7)——装饰者模式
  9. 【有限差分法】(一)有限差分法的基本流程与常用格式
  10. TikTok广告投放必备指南
  11. win10装inventor哪个版本_超详细!高效电脑装系统教程
  12. 雷电模拟器连接hb_原生的安卓模拟器来了,微软发布Your Phone,与三星独家合作...
  13. 中值定理5-泰勒中值定理
  14. (二)大话深度学习编译器中的自动调优·DSL与IR
  15. 吃饭 睡觉 打豆豆!!!
  16. js中字符串截取函数及其方法
  17. 基于B/S的校园餐厅网上订餐系统
  18. 苹果屏保壁纸_神经病手势时钟-手势数字时钟app下载android安卓版ios苹果版
  19. tkinter style样式使用
  20. kafka可靠性保证

热门文章

  1. vue核心面试题:v-for中为什么要用key
  2. 什么是jQuery,jQuery的特点。
  3. 【Linux】SSH相关命令
  4. python程序设计是什么专业-那门用Python讲授的程序设计课程能带给学生什么?
  5. 训练ChatGPT的必备资源:语料、模型和代码库完全指南
  6. Linux 命令(217)—— iptables-restore 命令
  7. 保定学院数学与计算机系2016,保定学院数学与计算机系
  8. Linux常用命令(适合初学者)
  9. python可以用保留字作为函数的名字吗_Python不允许使用关键字作为变量名,允许使用内置函数名作为变量名,但这会改变函数名的含义...
  10. redux及react-redux