欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MyBatis学习笔记(一)

程序员文章站 2022-05-23 23:54:18
...

MyBatis概述

MyBatis本是Apache的一个开源项目iBatis, 2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。

MyBatis 是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、 创建connection、创建 statement、手动设置参数、结果集检索等 jdbc繁杂的过程代码。

Mybatis 通过 xml 或注解的方式将要执行的各种 statement(statement、 preparedStatemnt、CallableStatement)配置起来,并通过 java 对象和 statement 中的 sql 进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

使用JDBC存在的问题

JDBC的编程步骤

  1. 加载数据库驱动
  2. 创建获取数据库连接
  3. 创建Statement对象
  4. 执行SQL语句并返回ResultSet结果集
  5. 处理结果集
  6. 释放资源

存在的问题

  1. 数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能。如果使用数据库连接池可解决此问题。
  2. SQL语句在代码中硬编码,造成代码不易维护,实际应用中SQL变化的可能较大,SQL语句变动需要改变 java代码。
  3. 使用preparedStatement向占位符传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护。
  4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便。

MyBatis架构

MyBatis架构图

MyBatis学习笔记(一)

架构图解释

MyBatis配置文件

  • SqlMapConfig.xml

    MyBatis的全局配置文件,配置了MyBatis的运行环境等信息

  • SqlSessionFactory

    会话工厂,用于创建SqlSession,通过MyBatis环境等配置信息构造SqlSessionFactory

  • SqlSession

    会话,操作数据库主要通过SqlSession进行

  • Executor

    MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器

  • MappedStatement

    Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。

    • 输入映射

      Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数

    • 输出映射

      Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程

Mybatis入门程序

MyBatis下载

MyBatis下载地址

点击上述链接进入页面后,再点击下图中的位置即可下载;下载完毕后压缩包中有MyBatis的依赖包(在lib目录下)。

MyBatis学习笔记(一)

环境配置

  1. 导入MyBatis依赖包以及数据库连接驱动

  2. 编写SqlMapConfig.xml配置文件,配置文件的编写如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration PUBLIC
       "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <!--  与spring整合以后,environments配置将被废除  -->
    <environments default="development">
       <environment id="development">
           <!--事务管理,使用jdbc-->
           <transactionManager type="jdbc"></transactionManager>
           <!--数据库连接池-->
           <dataSource type="POOLED">
               <!--配置数据库连接信息-->
               <property name="driver" value="com.mysql.jdbc.Driver"></property>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?characterEncoding=UTF-8"></property>
               <property name="username" value="数据库用户名"></property>
               <property name="password" value="数据库密码"></property>
           </dataSource>
       </environment>
    </environments>
    </configuration>
    
  3. 编写日志输出文件log4j.properties,日志输出文件的编写如下:

    # Global logging configuration
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    
  4. 在数据库中创建表,我创建的是user表:

    CREATE TABLE `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(32) NOT NULL COMMENT '用户名称',
    `birthday` date DEFAULT NULL COMMENT '生日',
    `sex` char(1) DEFAULT NULL COMMENT '性别',
    `address` varchar(256) DEFAULT NULL COMMENT '地址',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
    
  5. 编写数据库中表的对应的pojo(Plain Ordinary Java Object,其实就是Java Bean)

  6. 编写UserMapper.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">
    <!--
    namespace:命名空间,用于隔离SQL
    ->
    <mapper namespace="com.xurenyi.pojo.User">
    <!--
       id:sql的id
       parameterType:输入参数的类型
       resultType:输出结果的类型,应该填写对应POJO的全路径
       #{v}:占位符,相当于jdbc中的?
    -->
    <select id="findUserById" parameterType="Integer"   resultType="com.xurenyi.pojo.User">
       select * from user where id=#{v}
    </select>
    </mapper>
    
  7. 在SqlMapperConfig.xml中配置UserMapper文件,写法如下:

    <!--配置UserMapper.xml文件的位置-->
    <mappers>
       <mapper resource="config/UserMapper.xml"></mapper>
    </mappers>
    

案例

根据id查询单个用户

  1. 在UserMapper.xml中编写SQL语句:
    <!--
    mapper:用于编写SQL语句,mapper元素中可以写多个SQL语句
    namespace:命名空间,用于隔离SQL
    -->
    <mapper namespace="com.xurenyi.pojo.User">
    <!--
       id:SQL语句的id
       parameterType:输入参数的类型
       resultType:输出结果的类型,应该填写对应POJO的全路径
       '#{v}':占位符,相当于jdbc中的?
    -->
    <!--
       根据id查询单个用户
    -->
    <select id="findUserById" parameterType="Integer" resultType="com.xurenyi.pojo.User">
       select * from user where id=${value}
    </select>
    </mapper>
    
  2. 编写测试方法:
    //根据id查找单个用户
    @Test
    public void findUserById() throws IOException {
       //加载SqlMapConfig.xml配置文件,把配置文件的路径作为参数传入
       InputStream in =  Resources.getResourceAsStream("config/SqlMapConfig.xml");
       //创建SqlSessionFactoryBuilder
       SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
       //获取SqlSessionFactory(根据配置文件创建SqlSessionFactory)
       SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
       //获取SqlSession
       SqlSession sqlSession = sqlSessionFactory.openSession();
       //执行select操作,传入参数是SQL语句的id、SQL语句中的查找参数
       User user = sqlSession.selectOne("findUserById", 1);
       System.out.println(user);
    }
    

注意: 由于下面的例子中测试代码的前面部分与根据id查询单个用户中的测试代码是一样的,所以在下述的例子中,就只写SQL语句(写在<mapper></mapper>元素中)以及通过sqlSession进行操作数据库的语句(例如根据id查询单个用户测试代码中的User user = sqlSession.selectOne("findUserById", 1);这句),省略获取SqlSession对象的代码。

根据用户名模糊查询用户

  • 方式一
    • SQL语句
      <!--模糊查询-->
      <select id="findUserListByLike"
             parameterType="String"
             resultType="com.xurenyi.pojo.User">
         select * from user where username like #{v}
      </select>
      
    • 测试代码
      /*
      获取SqlSession对象的代码,略
      */
      List<User> userList = sqlSession.selectList("findUserListByLike", "%张%");
      
  • 方式二
    • SQL语句
      <!--模糊查询-->
      <select id="findUserListByLike"
             parameterType="String"
             resultType="com.xurenyi.pojo.User">
         select * from user where username like '%${value}%'
      </select>
      
    • 测试代码
      /*
      获取SqlSession对象的代码,略
      */
      List<User> userList = sqlSession.selectList("findUserListByLike", "张");
      
  • 方式三
    • SQL语句
      <!--模糊查询-->
      <select id="findUserListByLike"
             parameterType="String"
             resultType="com.xurenyi.pojo.User">
         select * from user where username like "%"#{v}"%"
      </select>
      
    • 测试代码
      /*
      获取SqlSession对象的代码,略
      */
      List<User> userList = sqlSession.selectList("findUserListByLike", "张");
      

添加用户

  • SQL语句
    <!--添加用户
    参数类型是User对象,所以在SQL语句中取参数值的时候,需要把User实体类里面的属性名写进去,而不能在'#{}'里面随便写几个字母
    -->
    <insert id="addUser" parameterType="com.xurenyi.pojo.User">
       <!--取参数的时候需要把User里面的属性写进'#{}',而不能随便写-->
       insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
    </insert>
    
  • 测试代码
    /*
    获取SqlSession对象的代码,略
    */
    User user = new User();
    user.setId(null);
    user.setUsername("用户名");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setAddress("用户地址");
    // 需要"改变"数据库中数据的操作(例如:插入、删除、修改数据)都有一个int类型的返回值,该返回值表示对数据库中记录影响的行数
    int addUser = sqlSession.insert("addUser", user);
    sqlSession.commit();   //需要自己提交事务(在增删改查这四个操作中,除了查询操作,其余三个操作都需要提交事务)
    

添加用户返回ID

  • 需求:在使用MyBatis插入一个用户数据以后,获取该用户的id。
  • select LAST_INSERT_ID();
    • 这是MySQL中提供的查询最新添加数据的id,但是执行该条语句时,需要先执行一条插入语句才能返回id,否则就返回0。
  • SQL语句
    <insert id="addUser" parameterType="com.xurenyi.pojo.User">
       <!--
           标签实现主键返回
           keyColumn:主键对应数据库表中的哪一列
           keyProperty:主键对应pojo中的哪一个属性
           order:设置在执行insert语句之前执行查询id的操作,还是在执行insert语句之后执行查询id的操作
       -->
       
       <selectKey keyColumn="id" keyProperty="id" resultType="Integer" order="AFTER">
           select LAST_INSERT_ID()
       </selectKey>
       
       <!--取参数的时候需要把User里面的属性写进#{},而不能随便写-->
       insert into user values(#{id},#{username},#{birthday},#{sex},#{address})
    </insert>
    
  • 测试代码
    /*
    获取SqlSession对象的代码,略
    */
    User user = new User();
    user.setId(null);
    user.setUsername("用户名");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setAddress("用户地址");
    // 需要"改变"数据库中数据的操作(例如:插入、删除、修改数据)都有一个int类型的返回值,该返回值表示对数据库中记录影响的行数
    int addUser = sqlSession.insert("addUser", user);
    sqlSession.commit(); //需要自己提交事务(在增删改查这四个操作中,除了查询操作,其余三个操作都需要提交事务)
    System.out.println(user.getId()); //在提交事务之后获取id并打印
    

根据id修改用户数据

  • SQL语句
    <!--根据id修改用户数据-->
    <update id="updateUserById" parameterType="com.xurenyi.pojo.User">
       update user set address=#{address},birthday=#{birthday}  where id=#{id}
    </update>
    
  • 测试代码
    /*
    获取SqlSession对象的代码,略
    */
    User user = new User();
    user.setId(28);
    user.setAddress("修改后的地址");
    user.setUsername("修改后的姓名");
    user.setSex("女");
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    Date date = simpleDateFormat.parse("1994-01-01 17:06:00");
    user.setBirthday(date);
    sqlSession.update("updateUserById", user);
    //提交事务
    sqlSession.commit();
    //关闭sqlSession
    sqlSession.close();
    

根据id删除用户

  • SQL语句
    <!--根据id删除用户数据-->
    <delete id="deleteUserById" parameterType="Integer">
       delete from user where id=#{id}
    </delete>
    
  • 测试代码
    /*
    获取SqlSession对象的代码,略
    */
    int count=sqlSession.delete("deleteUserById",28);
    //提交事务
    sqlSession.commit();
    System.out.println(count);
    //关闭sqlSession
    sqlSession.close();
    

小结

  • #{}和${}的区别
    • #{}
      #{}表示一个占位符号,通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换。#{}可以有效防止sql注入。 #{}可以接收简单类型值或pojo属性值。 如果parameterType传输单个简单类型值, #{}括号中可以是 value 或其它名称。
    • $ {}
      $ {}表示拼接 sql 串,通过$ {}可以将parameterType传入的内容拼接在sql中且不进行 jdbc类型转换,$ {}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,${}括号中只能是 value。
  • parameterType和resultType
    • parameterType
      指定输入参数类型,mybatis通过ognl从输入对象中获取参数值拼接在 sql 中。
    • resultType
      指定输出结果类型,mybatis 将 sql 查询结果的一行记录数据映射为resultType 指定类型的对象。如果有多条数据,则分别进行映射,并把对象放到容器 List 中。
  • selectOne和selectList
    • selectOne
      selectOne用于查询一条记录,如果使用selectOne查询多条记录会报错。
    • selectList
      selectList用于查询一条或多条记录。
  • MyBatis解决JDBC编程的问题
    • 问题1

      • 描述:数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库连接池可解决此问题
      • 解决:在 SqlMapConfig.xml 中配置数据连接池,使用连接池管理数据库链接
    • 问题2

      • 描述:Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java代码
      • 解决:将 Sql 语句配置在 XXXXmapper.xml 文件中与 java 代码分离
    • 问题3

      • 描述:向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应
      • 解决:Mybatis 自动将 java 对象映射至 sql 语句,通过 statement 中的 parameterType 定义输入参数的类型
    • 问题4

      • 描述:对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果 能将数据库记录封装成pojo对象解析比较方便
      • 解决:Mybatis自动将sql执行结果映射至java对象,通过statement中的 resultType定义输出结果的类型
  • MyBatis和Hibernate的异同
    • Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句。mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。
    • Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如:互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一旦需求变化要求成果输出迅速。但是灵活的前提是 mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套 sql 映射文件,工作量大。
    • Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。
    • 总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。

SqlMapConfig.xml配置文件详解

SqlMapConfig.xml中配置的内容和顺序

MyBatis学习笔记(一)
这里选讲几个元素。

  • properties(属性)

    • properties是用来加载外部配置文件的,其写法如下:

      <!--使用resource加载外部配置文件-->
      <properties resource="外部配置文件的路径">
         <!--如果外部配置文件中有该属性,那么内部定义的属性被外部属性覆盖-->
         <property name="" value=""/>
      </properties>
      
    • MyBatis加载属性的顺序

      先读取properties元素体内自定义的属性,再读取properties中resource指定路径的外部配置文件中的属性,如果在外部配置文件中有同名属性,则后者会覆盖前者。

  • typeAliases(类型别名)

    有时候写实体类的全路径比较麻烦,所以可以取一个别名,这样可以在Mapper.xml中直接写别名调用。

    • 定义单个别名

      定义单个别名的配置写法如下:

      <typeAliases>
         <!--定义单个别名
             type:需要被取别名的对象全路径
             alias:对应的别名
         -->
         <typeAlias type="com.xurenyi.pojo.User" alias="user"></typeAlias>
      </typeAliases>
      
    • 批量定义别名

      批量定义别名的配置写法如下:

      <typeAliases>
        <!--批量定义别名
            会扫描整个包下的类,别名是类名(大小写不敏感)
        -->
        <package name="com.xurenyi.pojo"></package>
      
    ```
  • mappers(映射器)

    • 用法1

      <mapper resource="mapper.xml配置文件的全路径"></mapper>

    • 用法2
      <mapper class="对应的mapper类的路径"></mapper>

      此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

    • 用法3
      <package name="包路径"></mapper>

      此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
      实际项目开发中,会使用这种方法。

相关标签: java web框架