【Mybatis从入门到实战教程】第一章 Mybatis 入门

一、Mybatis 入门

1.1 什么是MyBatis

    MyBatis本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation迁移到了google code,并且改名为MyBatis。2013年11月迁移到Github。
    
    iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)。

    软件框架(software framework),通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。可以简单理解为框架是一个半成品的项目,我们可以基于框架进行项目开发,提高开发效率。现在互联网企业都是效率至上,如果开发周期过长,可能市场很快就会出现同类产品,失去先机。
    
    当前,最新版本是MyBatis 3.5.9,其发布时间是2021年12月26日。
    
    MyBatis是一个优秀的持久层框架,它对JDBC操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等JDBC繁杂的过程代码。
    
    MyBatis通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由mybatis框架执行sql并将结果映射成Java对象并返回。

1.2 MyBatis的优点

    简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件。易于学习,易于使用。通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

    灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。

    解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。

    提供映射标签,支持对象与数据库的orm字段关系映射。

    提供对象关系映射标签,支持对象关系组建维护。

    提供xml标签,支持编写动态sql。

1.3 JDBC编程存在的问题

    在实际开发过程中,我们一般使用ORM框架来代替传统的JDBC,例如Mybatis或者Hibernate,但JDBC是Java用来实现数据访问的基础,掌握它对于我们理解Java的数据操作流程很有帮助。

    ORM:对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。

    Java典型的ORM中间件有:Hibernate,Mybatis,speedframework。

    ORM技术特点:
        1、提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。 

        2、ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。 

    那为什么我们在实际开发中都是用一些ORM框架而不用传统的JDBC进行开发呢?接下来我们来看一下一个JDBC程序的简单实现并分析其缺点,如下:

1.3.1 JDBC程序

@Override
public void insertUser(User user) {
    Connection conn = DBUtil.getConnection();
    PreparedStatement stmt = null;
    String sql = "insert into sys_user(NAME,ACCT,PWD,CRTIME,UPTIME) values(?,?,?,now(),now())";
    try {
        stmt = conn.prepareStatement(sql);
        stmt.setString(1, user.getName());
        stmt.setString(2, user.getAcct());
        stmt.setString(3, user.getPwd());
        stmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        DBUtil.closeAll(conn, stmt, null);
    }
}

1.3.2 JDBC编程步骤

    1、加载数据库驱动;

    2、创建并获取数据库连接Connection;

    3、创建执行SQL语句的PreparedStatement对象;

    4、设置SQL语句中的占位符参数;

    5、通过PreparedStatement执行Sql并获取结果集;

    6、对Sql执行结果进行解析处理;

    7、释放资源(Connection、Preparedstatement、ResultSet)。

1.3.3 JDBC问题总结如下

从以上的程序中,进行总结如下:

    1、数据库连接使用时就创建,不使用时便立即释放,从而对数据库进行频繁的操作,导致资源的浪费、影响性能;
        优化设想:使用数据库连接池管理数据库对象;

    2、sql都是硬编码到Java程序中,如果改变sql,那么得重新编译Java代码,不利于系统后期的维护;
        优化设想:将sql语句配置在xml中,即使改变sql也不用重新编译源代码;

    3、向PreparedStatement设置参数,也是硬编码到Java程序中,不利于后期的维护;
        优化设想:将sql语句以及占位符和参数全部配置在xml中,改动也不需要重新编译源代码;

    4、从resultset遍历结果集数据时,也存在硬编码,不利于后期系统的维护;
        优化设想:将查询的结果集自动映射成Java对象;

    针对以上问题,顺其自然的就出现了许多优化JDBC的方案,也就是后期出现的ORM持久层框架,例如Mybatis以及Hibernate等等;这也就是为什么在实际开发中都比较喜欢用ORM框架的原因了。
    
    有了这个概念,那么接下来我们就开始对Mybatis进行学习...

    注:硬编码是将数据直接嵌入到程序或其他可执行对象的源代码中的软件开发实践,与从外部获取数据或在运行时生成数据不同。硬编码数据通常只能通过编辑源代码和重新编译可执行文件来修改。

1.4 MyBatis架构

    1、MyBatis配置:
        mybatis-config.xml(名称不固定),此文件作为MyBatis的全局(核心)配置文件,配置了MyBatis的运行环境等信息。
        mapper.xml文件即Sql映射文件,文件中配置了操作数据库的Sql语句。此文件需要在mybatis-config.xml中加载。

    2、通过MyBatis环境等配置信息构造SqlSessionFactory,即会话工厂。

    3、由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。

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

    5、MappedStatement也是MyBatis一个底层封装对象,Mybatis将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。mapper.xml文件中一个Sql对应一个MappedStatement对象,Sql的id即是MappedStatement的id。

    6、MappedStatement对Sql执行输入参数进行定义,包括HashMap、基本类型、字符串类型、实体类类型,Executor通过MappedStatement在执行Sql前将输入的Java对象映射至Sql中,输入参数映射就是JDBC编程中对PreparedStatement设置参数。

    7、MappedStatement对Sql执行输出结果进行定义,包括HashMap、基本类型、字符串类型、实体类类型,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射过程相当于JDBC编程中对结果的解析处理过程。

原理分析:

    1、加载配置:配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信息加载成为一个个MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存中。

    2、SQL解析:当API接口层接收到调用请求时,会接收到传入SQL的ID和传入对象(可以是Map、JavaBean或者基本数据类型),Mybatis会根据SQL的ID找到对应的MappedStatement,然后根据传入参数对象对MappedStatement进行解析,解析后可以得到最终要执行的SQL语句和参数。

    3、SQL执行:将最终得到的SQL和参数拿到数据库进行执行,得到操作数据库的结果。

    4、结果映射:将操作数据库的结果按照映射的配置进行转换,可以转换成HashMap、JavaBean或者基本数据类型,并将最终结果返回。

1.5 搭建MyBatis项目

1.5.1 建立Java项目

    MyBatis是一个持久层框架,是操作数据库时使用的。无需创建JavaWEB项目,建立Maven Java项目即可。

1.5.2 导入MyBatis框架jar包

    Mybaits的代码由github.com管理,下载地址:https://github.com/mybatis/mybatis-3/releases

mybatis目录结构:

    lib -> mybatis附属工具包

    LICENSE -> 许可证

    mybatis-3.4.6.jar -> mybatis的核心包
    
    mybatis-3.4.6.pdf -> mybatis使用手册

    NOTICE -> 法律声明

方式一:导入jar包

    mybatis-3.4.6.jar为核心jar包,必须引入。
    
    lib目录下的jar包为工具包,可有可无,我们一般会引入log4j作为mybatis日志输出组件。

    mysql或oracle数据库的驱动包,必须引入。  

方式二:maven依赖配置

<dependencies>
    <!-- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <!-- oracle:大家在引入oracle依赖的时候肯定出错 -->
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0.1.0</version>
    </dependency>
    <!--  mysql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    <!-- log4j Mybatis的日志输出组件 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

    oracle依赖报错解决:https://blog.csdn.net/qq_38000902/article/details/82759268

    在oracle安装目录里面就有ojdbc6.jar:D:\Tools\product\11.2.0\dbhome_1\jdbc\lib(我的路径)

    mybatis默认使用log4j作为输出日志信息,你也可以引入log4j相关文件。

1.5.3 编写MyBatis中全局配置文件

    在src/main/resources目录下创建mybatis-config.xml,作用:配置了数据源、事务等MyBatis运行环境等。

    注:如果src/main下面没有resources目录,那么我们手动创建一个,让其成为Resources Root。

<?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 文件的根节点 -->
<configuration>
    <!--
      properties 用于引入外部的properties配置文件
      resource:引入类路径下的文件
      url:引入磁盘或网路
     -->
    <properties/>

    <!-- environments:多个配置环境;通过default属性可以对多个环境快速切换 -->
    <!-- environments default属性的值必须和某个environment的id值一致 -->
    <!-- 和spring整合后 environments配置将废除,了解即可 -->
    <environments default="mysql">
        <environment id="oracle">
            <!-- 配置事务:使用jdbc的事务管理 -->
            <transactionManager type="JDBC"/>
            <!-- 配置数据源:连接数据库的信息
                type: 表示连接是否使用连接池,POOLED表示mybatis中自带的连接池
    JNDI、POOLED、UNPOOLED
             -->
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl"/>
                <property name="username" value="scott"/>
                <property name="password" value="tiger"/>
            </dataSource>
        </environment>
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?characterEncoding=utf8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

MyBatis配置文件报错:

    在编写xml配置文件引入DTD约束时,可能会出现这个错误提示 URI is not registered(Settings | Languages & Frameworks | Schemas and DTDs),此时使用快捷键,选择Fetch external resource 或 Ignore external resource选项。

    或者直接在设置settings -> languages&frameworks -> schemas and dtds 中添加出问题的路径。

1.5.4 数据库SQL

CREATE TABLE `dept`  (
  `deptno` int PRIMARY KEY AUTO_INCREMENT,
  `dname` varchar(20),
  `loc` varchar(40)
);

INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');

CREATE TABLE `emp`  (
  `empno` int PRIMARY KEY AUTO_INCREMENT,
  `ename` varchar(20),
  `job` varchar(20),
  `mgr` int,
  `hiredate` date,
  `sal` double,
  `comm` double,
  `deptno` int,
  CONSTRAINT `FK_EMP_DEPTNO` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`)
);

INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 1300, NULL, 20);
INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 2100, 300, 30);
INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1750, 500, 30);
INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 3475, NULL, 20);
INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1750, 1400, 30);
INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 3350, NULL, 30);
INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2950, NULL, 10);
INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3500, NULL, 20);
INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5500, NULL, 10);
INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 2000, 0, 30);
INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1600, NULL, 20);
INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '0198-12-31', 1450, NULL, 30);
INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3500, NULL, 20);
INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1800, NULL, 10);

1.5.5 编写实体类

    实体类作为Mybatis进行sql映射使用,实体类通常与数据库表对应,Emp.java如下:

    注:实体类是用来和数据库表对应的,我们最好全部使用引用类型。

package com.newcapec.entity;

import java.util.Date;

public class Emp {

    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;

    public Integer getEmpno() {
        return empno;
    }

    public void setEmpno(Integer empno) {
        this.empno = empno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public Integer getMgr() {
        return mgr;
    }

    public void setMgr(Integer mgr) {
        this.mgr = mgr;
    }

    public Date getHiredate() {
        return hiredate;
    }

    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }

    public Double getSal() {
        return sal;
    }

    public void setSal(Double sal) {
        this.sal = sal;
    }

    public Double getComm() {
        return comm;
    }

    public void setComm(Double comm) {
        this.comm = comm;
    }

    public Integer getDeptno() {
        return deptno;
    }

    public void setDeptno(Integer deptno) {
        this.deptno = deptno;
    }

    @Override
    public String toString() {
        return "Emp{" +
            "empno=" + empno +
            ", ename='" + ename + '\'' +
            ", job='" + job + '\'' +
            ", mgr=" + mgr +
            ", hiredate=" + hiredate +
            ", sal=" + sal +
            ", comm=" + comm +
            ", deptno=" + deptno +
            '}';
    }
}

1.5.6 编写映射文件

    在src/main/resources下创建mapper目录,在该目录下创建sql映射文件Emp.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: 命名空间,作用是mapper文件进行分类管理,用于隔离sql语句。
    注意:如果使用mapper代理的方式进行开发,namespace有特殊的作用。
-->
<mapper namespace="emp">
    <!-- 在映射文件中编写sql语句 -->
    <!-- 通过员工编号查询员工信息 -->
    <!--
        通过<select>标签编写查询语句
        id: 映射文件中SQL语句的唯一标识
             mybatis会将SQL语句封装到MappedStatement对象中,所以此处的id也可以标识MappedStatement对象的id
             注意:同一个mapper文件中id不能重复,而且id在mapper代理模式下有着重要作用
        parameterType: 输入参数的类型
            sql语句的占位符:#{}
            #{empno}:其中empno表示接收输入的参数值,参数名称为empno
			但是如果参数的类型为简单类型(基本数据类型、包装类、字符串类型),参数名称可以任意指定
		resultType: 输出参数的类型
			需要指定输出数据为Java中的数据类型(实体类的全限定名)
    -->
    <select id="selectById" parameterType="java.lang.Integer" resultType="com.newcapec.entity.Emp">
        select empno,ename,job,hiredate,mgr,sal,comm,deptno from emp where empno=#{empno}
    </select>
</mapper>

1.5.7 加载映射文件

    在MyBatis的全局配置文件中添加映射文件位置。

<!-- 加载映射文件的位置 -->
<mappers>
    <mapper resource="mapper/Emp.xml"/>
</mappers>

1.5.8 log4j配置

    Mybatis日志输出:log4j.properties配置文件。

#井号表示注释,配置内容为键值对格式,每行只能有一个键值对,键值对之间以=连接
#指定logger
#设定log4j的日志级别和输出的目的地
#INFO日志级别 ,Console和logfile输出的目的地
#等级 OFF,ERROR,WARN,INFO,DEBUG,TRACE,ALL
log4j.rootLogger=DEBUG,Console

#指定appender
#设定Logger的Console,其中Console为自定义名称,类型为控制台输出
log4j.appender.Console=org.apache.log4j.ConsoleAppender

#设定Logger的logfile,其中logfile为自定义名称,类型为文件
#org.apache.log4j.FileAppender文件
#org.apache.log4j.RollingFileAppender文件大小到达指定尺寸后产生一个新的文件
#org.apache.log4j.DailyRollingFileAppender每天产生一个日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
#设定文件的输出路径
log4j.appender.logfile.File=d:/log/test.log
#设定文件最大尺寸  单位可以使KB,MB,GB
log4j.appender.logfile.MaxFileSize=2048KB

#输出格式
#设定appender布局Layout
#   %d 输出日志的日期和时间,指定格式:%d{yyyy-MM-dd HH:mm:ss SSS}
#   %p 输出的日志级别
#   %c 输出所属类的全类名
#   %M 方法名
#   %m 输出代码中指定消息
#   %n 一个换行符
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d %p %c.%M() --%m%n
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p %c.%M() --%m%n

1.5.9 编写测试程序

    推荐使用Junit单元测试。

package com.newcapec;

import com.newcapec.entity.Emp;
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.IOException;
import java.io.InputStream;

/**
 * 测试程序
 */
public class MybatisTest {

    @Test
    public void test() throws IOException {
        //1.创建读取全局配置文件的流
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();

        //2.通过配置文件流创建会话工厂
        SqlSessionFactory factory = builder.build(in);

        //3.通过会话工厂创建会话对象(SqlSession)
        SqlSession session = factory.openSession();

        //4.通过会话对象操作数据
        /**
         * 查询单条记录
         * selectOne(String statementId, Object param)
         * 参数1:映射文件中的statementId,命名空间名.statementId
         * 参数2:向sql语句中传入的数据,注意:传入的数据类型必须与映射文件中配置的parameterType保持一致
         * 返回值:就是映射文件中配置的resultType的类型
         * 查询多条记录
         * selectList()
         */
        Emp emp = session.selectOne("emp.selectById", 7369);
        System.out.println(emp);
        //5.关闭资源
        session.close();
    }
}

1.6 增删改查的基本操作

    实现以下功能:
        查询所有员工信息

        添加员工

        更新员工

        删除员工

        根据员工名模糊查询

1.6.1 查询操作

mapper文件:

<!--
	查询到数据返回多条记录,每一条封装在一个实体类对象中,所有的实体类对象封装在List集合中
	resultType:指定的并不是集合的类型,而是单条数据所对应实体类类型
	resultType="java.util.List" 错误的配置方式
-->
<select id="select" resultType="com.newcapec.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp order by empno desc
</select>

Java代码:

public class CURDTest {

    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();

        /**
         * 查询多条记录
         * selectList(statement-sql语句的id, parameter-参数)
         * 表示将单条记录都存储输出映射对象中,每条记录的映射对象存放在List集合中
         */
        List<Emp> list = session.selectList("emp.select");
        for (Emp emp : list) {
            System.out.println(emp);
        }
        session.close();
    }
}

1.6.2 新增操作

mapper文件:

<!--
	添加操作使用insert标签
	增删改操作没有resultType,只有查询有resultType;
	因为增删改操作返回值都是int类型,所以我们不需要指明

	注意:给占位符赋值,#{}中编写的内容为实体类型参数中的成员变量名称
		#{empno}    Mybatis会从传递过来的参数对象里面得到emono字段的值
-->
<insert id="insert" parameterType="com.newcapec.entity.Emp">
    insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
    values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>

Java代码:

@Test
public void testInsert() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();

    Emp emp = new Emp();
    emp.setEname("TOM");
    emp.setJob("CLARK");
    emp.setMgr(1);
    emp.setHiredate(new Date());
    emp.setSal(6500.0);
    emp.setComm(1200.0);

    System.out.println("新增之前的主键值为:" + emp.getEmpno());
    
    int result = session.insert("emp.insert", emp);
    
    System.out.println("影响数据库的条数为:" + result);
    /**
     * mybatis中的事务是jdbc的事务机制,mybatis里面默认是手动提交
     */
    session.commit();
    
    System.out.println("新增之后的主键值为:" + emp.getEmpno());

    session.close();
}

插入数据的主键返回:

  • select last_insert_id(),表示得到刚insert进去记录的主键值,适用与自增主键的数据库;

  • select seq_demo.nextval from dual,表示获取下一个序列生成的值,适用于存在序列的数据库;

  • keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性;

  • order:selectKey标签中Sql语句,相对于insert语句来说的执行顺序;

  • resultType:指定selectKey标签中Sql语句的结果类型;

Oracle数据库:

<insert id="insert" parameterType="com.newcapec.entity.Emp">
    <selectKey resultType="java.lang.Integer" keyProperty="empno" order="BEFORE">
        select seq_demo.nextval from dual
    </selectKey>
    insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
    values(#{empno},#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>

MySQL数据库:

<insert id="insert" parameterType="com.newcapec.entity.Emp">
    <selectKey resultType="java.lang.Integer" keyProperty="empno" order="AFTER">
        select last_insert_id()
    </selectKey>
    insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
    values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>

如果是主键自增型数据库(MySQL)还可以使用:

  • useGeneratedKeys: 是否开启主键返回,默认不开启false;

  • keyProperty: 获取到主键值后存放在实体类哪个成员变量中;

<insert id="insert" parameterType="com.newcapec.entity.Emp" useGeneratedKeys="true" keyProperty="empno">
    insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
    values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
</insert>

1.6.3 修改操作

mapper文件:

<update id="update" parameterType="com.newcapec.entity.Emp">
    update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#{hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno} where empno=#{empno}
</update>

Java代码:

@Test
public void testUpdate() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();

    Emp emp = new Emp();
    emp.setEmpno(7936);
    emp.setEname("JERRY");
    emp.setJob("MANAGER");
    emp.setMgr(7698);
    emp.setHiredate(new Date(new Date().getTime() + 1000*60*60*24));
    emp.setSal(7800.0);
    emp.setComm(800.0);

    int result = session.update("emp.update", emp);
    System.out.println("影响数据库的条数为:" + result);
    
    session.commit();

    session.close();
}

1.6.4 删除操作

mapper文件:

<delete id="delete" parameterType="java.lang.Integer">
    delete from emp where empno=#{empno}
</delete>

Java代码:

@Test
public void testDelete() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();

    int result = session.delete("emp.delete", 7935);
    System.out.println("影响数据库的条数为:" + result);

    session.commit();

    session.close();
}

1.6.5 模糊查询

mapper文件:

<!--
	条件查询:模糊查询
    1、#{}占位符,防止sql注入
    需要在Java中将传入数据的前后拼接%符号
    where ename like #{ename}
    
	2、使用字符串拼接函数
    where ename like concat('%',#{ename},'%')

    3、${}拼接符号,实现sql的拼接
    where ename like '%${value}%'
    注意:${}不是占位符,如果输入参数为简单类型,${}中的内容必须为value
-->
<select id="selectByEname1" parameterType="java.lang.String" resultType="com.newcapec.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like #{ename}
</select>

Java代码:

@Test
public void testSelectByEname() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();
    String ename = "S";
    List<Emp> list = session.selectList("emp.selectByEname1", "%"+ename+"%");
    for (Emp emp : list) {
        System.out.println(emp);
    }
    session.close();
}

mapper文件:

<select id="selectByEname2" parameterType="java.lang.String" resultType="com.newcapec.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like concat('%',#{ename},'%')
</select>

Java代码:

@Test
public void testSelectByEname2() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();
    String ename = "S";
    List<Emp> list = session.selectList("emp.selectByEname2", ename);
    for (Emp emp : list) {
        System.out.println(emp);
    }
    session.close();
}

mapper文件:

<select id="selectByEname3" parameterType="java.lang.String" resultType="com.newcapec.entity.Emp">
    select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
    where ename like '%${value}%'
</select>

Java代码:

@Test
public void testSelectByEname3() throws IOException {
    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = factory.openSession();
    String ename = "S";
    List<Emp> list = session.selectList("emp.selectByEname3", ename);
    for (Emp emp : list) {
        System.out.println(emp);
    }
    session.close();
}

1.6.6 总结

parameterType和resultType:

    parameterType:指定输入参数类型,MyBatis通过ognl从输入对象中获取参数值设置在Sql中;

    resultType:指定输出结果类型,MyBatis将Sql查询结果的一行记录数据映射为resultType指定类型的对象;

#{} 和 ${}:

    #{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
    #{}接收简单类型,#{}中可以写成value或其它名称;
    #{}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值;
  
    ${}表示一个拼接符号,会引用Sql注入,所以不建议使用${};
    ${}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
    ${}接收简单类型,${}中只能写成value;
    ${}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值;

selectOne和selectList:

    selectOne表示查询出一条记录进行映射;
    如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象);
    
    selectList表示查询出一个列表(多条记录)进行映射;
    如果使用selectList查询多条记录,不能使用selectOne ;
    如果使用selectOne报错:org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4  

1.7 MyBatis和Hibernate区别

    Hibernate:是一个标准ORM框架(对象关系映射)。
        入门门槛较高的,不需要程序写Sql,Sql语句自动生成,对sql语句进行优化、修改比较困难的;
        应用场景:适用与需求变化不多的中小型项目,比如:后台管理系统,erp、crm、oa...;

    MyBatis:专注于Sql本身,需要程序员自己编写Sql语句,Sql修改、优化比较方便;
        MyBatis是一个不完全的ORM框架,虽然程序员自己写Sql,MyBatis也可以实现映射(输入映射、输出映射);
        应用场景:适用与需求变化较多的项目,比如:互联网项目  

    企业进行技术选型,以低成本高回报作为技术选型的原则,根据项目组的技术力量进行选择。

    Mybatis和hibernate不同,它不完全是一个ORM框架,因为MyBatis需要程序员自己编写Sql语句,不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。

    Mybatis学习门槛低,简单易学,程序员直接编写原生态sql,可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。

    Hibernate对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如需求固定的定制化软件)如果用hibernate开发可以节省很多代码,提高效率。但是Hibernate的学习门槛高,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡,以及怎样用好Hibernate需要具有很强的经验和能力才行。

    总之,按照员工的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。

1.8 Mybatis解决JDBC编程的问题

    1、数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
        解决:在mybatis-config.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定义输出结果的类型。

猜你喜欢

转载自blog.csdn.net/ligonglanyuan/article/details/124272162