项目管理与SSM框架(一)| Mybatis

什么是ORM框架?

ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。
使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。

什么是MyBatis?

MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。

补充:
Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。

MyBatis与Hibernate的比较:

  • MyBatis是一个半自动的ORM框架,需要手写SQL语句。
  • Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
  • 使用MyBatis的开发量要大于Hibernate。

为什么Hibernate市场占有率越来越低:

  • 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
  • Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
  • 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
  • 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。

MyBatis入门

环境搭建

1. 准备一个SQL文件导入数据库
2. 创建一个Maven工程

<dependencies>
    
    <dependency>
        <groupId>org.mybatisgroupId>
        <artifactId>mybatisartifactId>
        <version>3.5.7version>
    dependency>
    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <version>8.0.26version>
    dependency>
    
    <dependency>
    <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.10version>
    dependency>
    
    <dependency>
        <groupId>log4jgroupId>
        <artifactId>log4jartifactId>
        <version>1.2.12version>
    dependency>
    
    <dependency>
    	<groupId>com.alibabagroupId>
    	<artifactId>druidartifactId>
    	<version>1.1.21version>
    dependency>
dependencies>

3. 创建mybatis核心配置文件SqlMapConfig.xml


DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  
  <environments default="development">
    <environment id="development">
      
      <transactionManager type="JDBC" />
      
      <dataSource type="com.alibaba.druid.pool.DruidDataSource">
        
        <property name="url" value="jdbc:mysql://localhost:3306/db_name" />
        <property name="username" value="your_username" />
        <property name="password" value="your_password" />

        
        <property name="initialSize" value="5" />   
        <property name="maxActive" value="20" />    
       
      dataSource>
    environment>
  environments>

  
  <mappers>
    <mapper resource="mapper/YourMapper.xml" />
    
  mappers>
configuration>

4. 将log4j.properties文件放入resources中,让控制台打印SQL语句。
5. 创建实体类

public class User {
    private int id;
    private String username;
    private String sex;
    private String address;
 	// 省略getter/setter/构造方法/toString方法
}

创建持久层接口和映射文件

1. 在java目录创建持久层dao包接口

public interface UserMapper {
    List<User> findAll();
}

2. 在resource目录创建映射文件*Mapper.xml文件


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gb.mapper.UserMapper">
    <select id="findAll" resultType="com.gb.pojo.User">
       select * from user
    select>
mapper>

3. 将映射文件配置到mybatis核心配置文件中


<mappers>
    <mapper resource="/mapper/UserMapper.xml">mapper>
mappers>

resource后是你的创建的*.Mapper的文件的包路径,可以创建一个mapper包将映射文件都放到里面

  • 映射文件要和接口名称相同。
  • 映射文件要和接口的目录结构相同。(可以通过配置修改)
  • 映射文件中namespace属性要写接口的全名。
  • 映射文件中标签的id属性是接口方法的方法名。(必须一致)
  • 映射文件中标签的resultType属性是接口方法的返回值类型。
  • 映射文件中标签的parameterType属性是接口方法的参数类型。
  • 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。(可以在配置文件中配置别名)
    paramterType 的参数对应的Java中类型的别名
    项目管理与SSM框架(一)| Mybatis_第1张图片

测试持久层接口方法

在test包中创建测试类 通过@Before 和@After 标签

public class UserMapperTest {

	InputStream is = null;
    SqlSession session = null;
    UserMapper userMapper = null;
    
    @Before
    public void before() throws IOException {
        // (1)读取核心配置文件
        is = Resources.getResourceAsStream("SqlMapConfig.xml");
        // (2)创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(is);
        // (4)SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
        // (5)获取代理对象
        userMapper = session.getMapper(UserMapper.class);
    }

    @After
    public void after() throws IOException {
        // 释放资源
        session.close();
        is.close();
    }
    // 测试方法
    @Test
    public void findAll(){
    	// 代理对象执行方法
		List<User> all = userMapper.findAll();
    	all.forEach(System.out::println);
	}
}

MyBatis核心对象及工作流程

项目管理与SSM框架(一)| Mybatis_第2张图片

MyBatis核心对象

  • SqlSessionFactoryBuilder
    SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。
  • SqlSessionFactory
    SqlSession工厂,使用工厂模式创建SqlSession对象。
  • SqlSession
    该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。
  • Mapper
    持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。

MyBatis工作流程

  1. 创建SqlSessionFactoryBuilder对象
  2. SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式
  3. SqlSessionFactory对象生产了SqlSession对象:工厂模式
  4. SqlSession对象创建了持久层接口的代理对象:动态代理模式
  5. 代理对象操作数据库

Mapper动态代理原理

我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。

获取代理对象

点开测试类的 getMapper 方法,查看该方法最终调用了什么方法。
项目管理与SSM框架(一)| Mybatis_第3张图片

当看到 Proxy.newProxyInstance 时,可以确定 getMapper 方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式

查看代理方式

点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。
项目管理与SSM框架(一)| Mybatis_第4张图片
可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。

结论:

  • SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
  • MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。

MyBatis模糊查询

使用$定义参数

模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
   select * from user where username like '%${value}%'
select>

#和$的区别:

  1. #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
  2. #可以防止sql注入,一般能用#就不用$。
  3. ${}内部的参数名必须写value。

使用定义参数

如果使用 # 还不想在调用方法的参数中添加 % ,可以使用 允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的Sql语句中。用法如下:

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
   <bind name="likeName" value="'%'+username+'%'"/>
   select * from user where username like # {likeName}
select>

MyBatis分页查询

顺序传参

不推荐使用

/**
     * 分页查询
     * @param startIndex 开始索引
     * @param pageSize 每页条数
     * @return
     */
List<User> findPage(int startIndex,int pageSize);

POJO传参

自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

public class PageQuery {
    private int startIndex;
    private int pageSize;
 // 省略getter/setter/构造方法
}
List<User> findPage2(PageQuery pageQuery);

Map传参

可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用
1. 持久层接口方法

List<User> findPage3(Map<String,Object> params);

2. 映射文件

<select id="findPage3" resultType="com.itbaizhan.pojo.User" parameterType="map">
   select * from user limit # {startIndex},#{pageSize}
select>

3. 测试类

@Test
public void testFindPage3(){
    Map<String,Object> params = new HashMap();
    params.put("startIndex",0);
    params.put("pageSize",4);
    List<User> users = userMapper.findPage3(params);
    users.forEach(System.out::println);
}

MyBatis聚合查询、主键回填

主键回填

有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。
1. 持久层接口方法

void add(User user);

2. 映射文件

<insert id="add" parameterType="com.gb.user.User">
    
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
       SELECT LAST_INSERT_ID();
    selectKey>
   insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
insert>

SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。

3. 测试类

@Test
public void testAdd(){
    User user = new User("尚学堂", new Date(), "男", "北京");
    userMapper.add(user);
    session.commit();
    System.out.println(user.getId());
}

MyBatis配置文件_

项目管理与SSM框架(一)| Mybatis_第5张图片

properties

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。
1. 编写db.properties

jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
jdbc.initialSize=5
jdbc.maxActive=20

2. 在配置文件中引入db.properties

 <properties resource="db.properties">properties>
  
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC">transactionManager>
            
            
            <dataSource type="com.alibaba.druid.pool.DruidDataSource">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
                <property name="initialSize" value="${jdbc.initialSize}"/> 
                <property name="maxActive" value="${jdbc.maxActive}"/>  
            dataSource>
        environment>
    environments>

MyBatis配置文件_

是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。

    <settings>
        
        
        
        <setting name="lazyLoadTriggerMethods" value=""/>
    settings>

MyBatis配置文件_

是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

	<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            
            <property name="helperDialect" value="mysql"/>
        plugin>
    plugins>

MyBatis配置文件_

MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用 设置自定义别名。

    <typeAliases>
        
        <package name="com.gb.pojo"/>

        
        
    typeAliases>

MyBatis配置文件_

可以为MyBatis配置数据环境。


    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC">transactionManager>
            
            
            <dataSource type="com.alibaba.druid.pool.DruidDataSource">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
                <property name="initialSize" value="${jdbc.initialSize}"/> 
                <property name="maxActive" value="${jdbc.maxActive}"/>  
            dataSource>
        environment>
    environments>

dataSource的type属性:

  • POOLED:使用连接池管理连接,使用MyBatis自带的连接池。
  • UNPOOLED:不使用连接池,直接由JDBC连接。
  • JNDI:由JAVAEE服务器管理连接,如果使用Tomcat作为服务器则使用Tomcat自带的连接池管理。

MyBatis配置文件_

用于注册映射文件或持久层接口,只有注册的映射文件才能使用,共有四种方式都可以完成注册:
1. 使用相对路径注册映射文件

<mappers>
  <mapper resource="/mapper/UserMapper.xml"/>
mappers>

这种方式比较好用
2. 使用绝对路径注册映射文件

<mappers>  
    <mapper url="file:///C:\Users\a\IdeaProjects\mybatiscase\mybatisDemo1\src\main\resources\com\gb\mapper\UserMapper.xml"/>
mappers>

3. 注册持久层接口

<mappers>  
    <mapper class="com.gb.mapper.UserMapper"/>
mappers>

4. 注册一个包下的所有持久层接口

<mappers>
    <package name="com.gb.mapper"/>
mappers>

MyBatis映射文件_

resultMap

标签的作用的自定义映射关系。
MyBatis可以将数据库结果集封装到对象中,是因为结果集的列名和对象属性名相同:
当POJO属性名和数据库列名不一致时,MyBatis无法自动完成映射关系。如:
项目管理与SSM框架(一)| Mybatis_第6张图片
此时有两种解决方案:
1. Sql语句的查询字段起与POJO属性相同的别名。

<select id="findAll" resultType="com.itbaizhan.pojo.Teacher">
   select tid as id,tname as teacherName from teacher;
select>

2 自定义映射关系
在映射文件中,使用 自定义映射关系:


<resultMap id="teacherMapper" type="com.itbaizhan.pojo.Teacher">
    
    <id property="id" column="tid">id>
    
    <result property="teacherName" column="tname">result>
resultMap>