Mybatis实战

一、前言

多数的ORM框架都把【增加】、【修改】与【删除】做得非常不错了,然而数据库中【查询】无疑是使用频次最高、复杂度大、与性能密切相关的操作,我们希望得到一种使用方便,查询灵活的ORM框架,MyBatis可以满足这些要求,MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架,它也是SSM(springmvc+spring+mybatis)框架集成中的重要组成部分。

1.1、ORM

ORM可以解决数据库与程序间的异构性,比如在Java中我们使用String表示字符串,而Oracle中可使用varchar2,MySQL中可使用varchar,SQLServer可使用nvarchar。

对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换

简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象与关系数据库相互映射。

没有ORM时我们是这样完成对象与关系数据库之间的映射的:

            //将执行的sql
            String sql = "SELECT name, id, age, password FROM users";
            //创建命令对象
            preparedStatement = connection.prepareStatement(sql);
            //执行并获得结果集
            resultSet = preparedStatement.executeQuery();
            //遍历结果集,将数据库中的数据转换成Java中的对象
            while(resultSet.next()){
                String name = resultSet.getString("name");
                int id = resultSet.getInt("id");
                int age = resultSet.getInt("age");
                String password = resultSet.getString("password");
                User entity= new User(name,id,age,password);
                Users.add(entity);
            }

这种方案存在以下不足: 

  • 持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口
  • 持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久化曾的相关程序代码,增加了软件的维护难度。
  • 将和数据库交互(CRUD)的代码硬编码到JDBC程序中
  • 实现健壮的持久化层需要高超的开发技巧,而且编程量很大(因为面向的是JDBC,偏底层)
  • 对象模型和关系模型的转换非常麻烦

ORM可以解决这些问题:

  • 一种将内存中的对象保存到关系型数据库中的技术
  • 负责实体域对象的持久化,封装数据库访问细节
  • ORM提供了实现持久化层的另一种模式,采用映射元数据(XML)来描述对象-关系的映射细节,使得ORM中间件能在任何一个Java应用的业务逻辑层和数据库之间充当桥梁。
  • ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。

1.2、Java领域典型的ORM

  • hibernate:全自动的框架,强大、复杂、笨重、学习成本较高
  • Mybatis:半自动的框架(懂数据库的人 才能操作,必须要自己写sql)
  • JPA:JPA全称Java Persistence API、JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,是Java自带的框架

1.3、ORM概念

ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。

面向对象概念面向关系概念
对象表的行(记录)
属性表的列(字段)

1.4、ORM优缺点

优点

  • 提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。
  • ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。

缺点

  • 牺牲程序的执行效率和会固定思维模式,降低了开发的灵活性。
  • 从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。
  • 在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。
  • 在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。
  • 但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。

二、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)

2.1、MyBatis的特点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。 sql写在xml里,便于统一管理和优化。通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的ORM字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供XML标签,支持编写动态sql。

2.2、MyBatis工作流程

(1)、加载配置并初始化

触发条件:加载配置文件

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

(2)、接收调用请求

触发条件:调用Mybatis提供的API

传入参数:为SQL的ID和传入参数对象

处理过程:将请求传递给下层的请求处理层进行处理。

(3)、处理操作请求 触发条件:API接口层传递请求过来 

传入参数:为SQL的ID和传入参数对象

处理过程:
  (A)根据SQL的ID查找对应的MappedStatement对象。

  (B)根据传入参数对象解析MappedStatement对象,得到最终要执行的SQL和执行传入参数。

  (C)获取数据库连接,根据得到的最终SQL语句和执行传入参数到数据库执行,并得到执行结果。

  (D)根据MappedStatement对象中的结果映射配置对得到的执行结果进行转换处理,并得到最终的处理结果。

  (E)释放连接资源。

(4)、返回处理结果将最终的处理结果返回。

无论是用过的hibernate,mybatis,你都可以发现他们有一个共同点:

在java对象和数据库之间有做mapping的配置文件,也通常是xml 文件

从配置文件(通常是XML配置文件中)得到 SessionFactory

由SessionFactory 产生 Session

在Session中完成对数据的增删改查和事务提交等

在用完之后关闭Session

2.3、MyBatis架构

Mybatis的功能架构分为三层:

  • API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
  • 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  • 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

2.4、MyBatis的主要成员及层次结构

ConfigurationMyBatis所有的配置信息都保存在Configuration对象之中,配置文件中的大部分配置都会存储到该类中
SqlSession作为MyBatis工作的主要顶层API,表示和数据库交互时的会话,完成必要数据库增删改查功能
ExecutorMyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数等
ParameterHandler负责对用户传递的参数转换成JDBC Statement 所对应的数据类型
ResultSetHandler负责将JDBC返回的ResultSet结果集对象转换成List类型的集合
TypeHandler负责java数据类型和jdbc数据类型(也可以说是数据表列类型)之间的映射和转换
MappedStatementMappedStatement维护一条<select|update|delete|insert>节点的封装
SqlSource负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql表示动态生成的SQL语句以及相应的参数信息

2.5、学习资源

mybatis3中文帮助:http://www.mybatis.org/mybatis-3/zh/index.html

mybatis-spring:http://www.mybatis.org/spring/zh/index.html

MyBatis中国分站:http://www.mybatis.cn/

源代码:https://github.com/mybatis/mybatis-3/

三、MyBatis快速入门示例

3.1、在IDEA中创建普通maven项目

3.2、添加依赖

下载地址:https://github.com/mybatis/mybatis-3/releases

如果是maven项目,pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mzj.mybatis</groupId>
  <artifactId>mybatis-demo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>mybatis-demo</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!--MyBatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <!--MySql数据库驱动 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.38</version>
    </dependency>
    <!-- JUnit单元测试工具 -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

3.3、创建数据库和表,针对MySQL数据库

SQL脚本如下:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `sex` enum('boy','girl','secret') DEFAULT 'secret',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

插入测试数据:

INSERT INTO `student` VALUES ('1', 'rose', 'girl');
INSERT INTO `student` VALUES ('2', 'jack', 'boy');
INSERT INTO `student` VALUES ('3', 'lili', 'girl');
INSERT INTO `student` VALUES ('4', 'candy', 'secret');

3.4、添加Mybatis配置文件

在Resources目录下创建一个conf.xml文件,如下图所示:

conf.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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/world"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/studentMapper.xml"/>
    </mappers>
</configuration>

说明如下:

3.5、定义表所对应的实体类

package com.mzj.mybatis.entitles;

/**student表对应的实体类
 *
 * @Auther: mazhongjia
 * @Date: 2020/6/6 10:44
 * @Version: 1.0
 */
public class Student {
    private int id;
    private String name;
    private String sex;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}

3.6、定义操作Student表的sql映射文件

在resources目录下创建一个mapper目录,专门用于存放sql映射文件,在目录中创建一个studentMapper.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">
<!-- 为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的
例如namespace="com.zhangguo.mybatis01.dao.studentMapper"就是com.zhangguo.mybatis01.dao(包名)+studentMapper(studentMapper.xml文件去除后缀)
-->
<mapper namespace="com.mzj.mybatis.dao.studentMapper">
    <!-- 在select标签中编写查询的SQL语句, 设置select标签的id属性为selectStudentById,id属性值必须是唯一的,不能够重复
 使用parameterType属性指明查询时使用的参数类型,resultType属性指明查询返回的结果集类型
resultType="com.zhangguo.mybatis01.entities.Student"就表示将查询结果封装成一个Student类的对象返回
Student类就是student表所对应的实体类
-->
    <!--
    根据id查询得到一个user对象
 -->
    <select id="selectStudentById" resultType="com.mzj.mybatis.entitles.Student">
        select * from student where id = #{id}
    </select>
</mapper>

3.7、在配置文件中注册映射文件

在配置文件conf.xml中注册studentMapper.xml映射文件

3.8、编写数据访问类

StudentDao.java,执行定义的select语句

package com.mzj.mybatis.dao;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/6 10:56
 * @Version: 1.0
 */
import com.mzj.mybatis.entitles.Student;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class StudentDao {

    public Student getStudentById(int id){
        //1、使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
        InputStream stream=StudentDao.class.getClassLoader().getResourceAsStream("conf.xml");
        //2、构建SqlSessionFactory工厂
        SqlSessionFactory ssf=new SqlSessionFactoryBuilder().build(stream);
        //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
        //Reader reader = Resources.getResourceAsReader(resource);
        //构建sqlSession的工厂
        //SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);

        //3、创建能执行映射文件中sql的sqlSession
        SqlSession session=ssf.openSession();
        /**
         * 映射sql的标识字符串,
         * com.mzj.mybatis.dao.studentMapper是studentMapper.xml文件中mapper标签的namespace属性的值,
         * selectStudentById是select标签的id属性值,通过select标签的id属性值就可以找到要执行的SQL
         */
        Student student=session.selectOne("com.mzj.mybatis.dao.studentMapper.selectStudentById",1);
        return student;
    }

 
}

  • mybatis配置
    • SqlMapConfig.xml(本例为conf.xml),此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息
    • mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
  • 通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
  • 由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
  • mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
  • 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编程中对结果的解析处理过程。

3.9、编写单元测试

package com.mzj.mybatis.dao;

import com.mzj.mybatis.entitles.Student;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/6 11:10
 * @Version: 1.0
 */
public class StudentDaoTest {

    @Before
    public void before() throws Exception {
    }

    @After
    public void after() throws Exception {
    }

    /**
     * Method: getStudentById(int id)
     */
    @Test
    public void testGetStudentById() throws Exception {
        StudentDao dao=new StudentDao();
        Student student=dao.getStudentById(1);
        System.out.println(student);
    }


}

运行:

四、基于XML映射实现完整数据访问

mybatis可以使用注解或者XML,两种方式进行配置。

XML:强大且可以解偶。

注解:方便且简单

因为每一个操作都需要先拿到会话,这里先定义一个工具类以便复用:

package com.mzj.mybatis.utils;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/10 12:23
 * @Version: 1.0
 */

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * MyBatis 会话工具类
 */
public class SqlSessionFactoryUtil {

    /**
     * 获得会话工厂
     */
    public static SqlSessionFactory getFactory() {
        InputStream inputStream = null;
        SqlSessionFactory sqlSessionFactory = null;

        try {
            //加载conf.xml配置文件,转换成输入流
            inputStream = SqlSessionFactoryUtil.class.getClassLoader().getResourceAsStream("conf.xml");

            //根据配置文件的输入流构造一个SQL会话工厂
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sqlSessionFactory;
    }

    /**
     * 获得sql会话,是否自动提交
     */
    public static SqlSession openSession(boolean isAutoCommit) {
        return getFactory().openSession(isAutoCommit);
    }

    /**
     * 关闭会话
     */
    public static void closeSession(SqlSession session) {
        if (session != null) {
            session.close();
        }
    }

}

mybatis的业务mapper.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">
<mapper namespace="com.mzj.mybatis.dao.studentMapperFull">
    <select id="selectStudentById" resultType="com.mzj.mybatis.entitles.Student">
        SELECT id,name,sex from student where id=#{id}
    </select>

    <select id="selectStudentsByName" parameterType="String" resultType="com.mzj.mybatis.entitles.Student">
      SELECT id,name,sex from student where name like '%${value}%';
    </select>

    <insert id="insertStudent" parameterType="com.mzj.mybatis.entitles.Student">
        insert into student(name,sex) VALUES(#{name},'${sex}')
    </insert>

    <update id="updateStudent" parameterType="com.mzj.mybatis.entitles.Student">
        update student set name=#{name},sex=#{sex} where id=#{id}
    </update>

    <delete id="deleteStudent" parameterType="int">
        delete from student where id=#{id}
    </delete>

</mapper>

mybatis配置文件增加业务mapper.xml

业务DAO

package com.mzj.mybatis.dao;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/10 12:27
 * @Version: 1.0
 */

import com.mzj.mybatis.entitles.Student;
import com.mzj.mybatis.utils.SqlSessionFactoryUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class StudentDaoFull {

    /**
     * 根据学生编号获得学生对象
     */
    public Student selectStudentById(int id) {
        Student entity = null;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil.openSession(true);

        //查询单个对象,指定参数为3
        entity = session.selectOne("com.mzj.mybatis.dao.studentMapperFull.selectStudentById", id);

        //关闭
        SqlSessionFactoryUtil.closeSession(session);

        return entity;
    }


    /**
     * 根据学生姓名获得学生集合
     */
    public List<Student> selectStudentsByName(String name) {
        List<Student> entities = null;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //查询多个对象,指定参数
        entities = session.selectList("com.mzj.mybatis.dao.studentMapperFull.selectStudentsByName", name);
        //关闭
        SqlSessionFactoryUtil.closeSession(session);
        return entities;
    }


    /**
     * 添加学生
     */
    public int insertStudent(Student entity) {
        //影响行数
        int rows = 0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //执行添加
        rows = session.insert("com.mzj.mybatis.dao.studentMapperFull.insertStudent", entity);
        //关闭
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 更新学生
     */
    public int updateStudent(Student entity) {
        //影响行数
        int rows = 0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //执行更新
        rows = session.update("com.mzj.mybatis.dao.studentMapperFull.updateStudent", entity);
        //关闭
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

    /**
     * 删除学生
     */
    public int deleteStudent(int id) {
        //影响行数
        int rows = 0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil.openSession(true);
        //执行删除
        rows = session.delete("com.mzj.mybatis.dao.studentMapperFull.deleteStudent", id);
        //关闭
        SqlSessionFactoryUtil.closeSession(session);
        return rows;
    }

}

测试用例

package com.mzj.mybatis.dao;

import com.mzj.mybatis.entitles.Student;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;

import java.util.List;

/**
 * StudentDao Tester.
 *
 * @author <Authors name>
 * @version 1.0
 * @since <pre>09/26/2018</pre>
 */
public class StudentDaoFullTest {

    StudentDaoFull dao;
    @Before
    public void before() throws Exception {
        dao=new StudentDaoFull();
    }

    @After
    public void after() throws Exception {
    }

    /**
     * Method: selectStudentById(int id)
     */
    @Test
    public void testSelectStudentById() throws Exception {
        Student entity=dao.selectStudentById(1);
        System.out.println(entity);
        Assert.assertNotNull(entity);
    }

    /**
     * Method: selectStudentsByName(String name)
     */
    @Test
    public void testSelectStudentsByName() throws Exception {
        List<Student> students=dao.selectStudentsByName("i");
        System.out.println(students);
        Assert.assertNotNull(students);
    }

    /**
     * Method: insertStudent
     */
    @Test
    public void testInsertStudent() throws Exception {
        Student entity=new Student();
        entity.setName("玛丽");
        entity.setSex("girl");

        Assert.assertEquals(1,dao.insertStudent(entity));
    }

    /**
     * Method: updateStudent
     */
    @Test
    public void testUpdateStudent() throws Exception {
        Student entity=dao.selectStudentById(3);
        entity.setName("马力");
        entity.setSex("boy");

        Assert.assertEquals(1,dao.updateStudent(entity));
    }

    /**
     * Method: deleteStudent
     */
    @Test
    public void testDeleteStudent() throws Exception {
        Assert.assertEquals(1,dao.deleteStudent(2));
    }
}

mybatis的mapper.xml编写说明及示例1:

<?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接口的全限定名一致 -->
<mapper namespace="com.san.mapper.UserMapper">

    <!-- 通过ID查询用户 -->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id=#{id}
    </select>

    <!-- 定义sql片段 -->
    <!-- sql片段内,可以定义sql语句中的任何内容 -->
    <!-- sql片段内,最好不要使用where和select关键字声明在内 -->
    <sql id="whereClause">
        <!-- if标签:对输入的参数进行判断 -->
        <!-- test标签:指定判断的表达式 -->
        <if test="user!=null">
            <!-- 判断用户名不为空 -->
            <if test="user.username!=null and user.username!=''">
                and username like '%${user.username}%'
            </if>

            <!-- 判断性别不为空 -->
            <if test="user.sex!=null and user.sex!=''">
                and sex=#{user.sex}
            </if>
        </if>

        <!-- 判断集合 -->
        <!-- collection:表示pojo中集合属性的属性名称 -->
        <!-- item:为遍历出的结果声明一个变量名称 -->
        <!-- open:遍历开始时,需要拼接的字符串 -->
        <!-- close:遍历结束时,需要拼接的字符串 -->
        <!-- separator:遍历中间需要拼接的字符串 -->
        <if test="idList!=null">
            and id in
            <foreach collection="idList" item="id" open="(" close=")" separator=",">
                <!-- and id in (#{id},#{id},#{id}) -->
                #{id}
            </foreach>
        </if>
    </sql>

    <!-- 综合查询,查询用户列表 -->
    <!-- #{}中的参数名称要和包装pojo中的对象层级一致,并且属性名称要一致 -->
    <select id="findUserList" parameterType="com.san.model.UserQueryvo" resultType="user">
        select * from user
        <!-- where标签:默认去掉后面第一个and,如果没有参数,则把自己干掉 -->
        <where> 
            <!-- 引入sql片段 -->
            <include refid="whereClause"></include>
        </where>
    </select>

    <!-- 综合查询,查询用户的总数 -->
    <select id="findUserCount" parameterType="com.san.model.UserQueryvo" resultType="int">
        select count(*) from user 
        <where>
            <include refid="whereClause"></include>
        </where>

    </select>

    <!-- id标签:专门为查询结果中唯一列映射 -->
    <!-- result标签:映射查询结果中的普通列 -->
    <!-- type标签:返回类型 -->
    <resultMap type="user" id="UserResMap">
        <id column="id_" property="id"/>
        <result column="username_" property="username"/>
        <result column="sex_" property="sex"/>
    </resultMap>    
    <select id="findUserRstMap" parameterType="int" resultMap="UserResMap">
        select id id_,username username_,sex sex_ from user where id=#{id}  
    </select>
</mapper>

mybatis的mapper.xml编写说明及示例2:

<?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进行分类化的管理,理解为sql隔离
    注意:使用mapper代理开发时,namespace有特殊作用
 -->
<mapper namespace="test">
<!-- 在映射文件中配置很多sql语句 -->
<!-- 需求:通过Id查询用户表的记录 -->
<!-- 通过SELECT执行数据库查询 
    id:标识映射文件中的sql,称为statement的id;
        将sql语句封装在mapperStatement的对象中,所以Id称为Statement的id;
    parameterType:指定输入参数的类型,这里指定int型
    #{}:表示一个占位符;
    #{id}:其中Id表示接收输入的参数,参数名称就是Id,如果输入参数是简单类型,#{}中的参数名可以任意,可以是value或者其它名称;
    resultType:指定sql输出结果所映射的java对象类型,select指定resultType表示将单条记录映射成java对象。
-->
<select id="findUserById" parameterType="int" resultType="com.mybatis.entity.User" >
    select * from t_user where id=#{id}
</select>
<!-- 根据用户名称模糊查询用户信息,可能返回多条数据
    resultType:指定的就是单条记录所映射的java类型;
    ${}:表示拼接sql字符串,将接收到的参数内容不加任何修饰拼接在sql中.
    使用${}拼接sql,可能会引起sql注入
    ${value}:接收输入参数的内容,如果传入的是简单类型,${}中只能使用value
 -->
<select id="findUserByName" parameterType="java.lang.String" resultType="com.mybatis.entity.User" >
    select * from t_user where username LIKE '%${value}%'
</select>
<!-- 添加用户 
parameterType:指定输入的参数类型是pojo(包括用户信息);
#{}中指定pojo的属性名称,接收到pojo对象的属性值    ,mybatis通过OGNL(类似struts2的OGNL)获取对象的属性值
-->
<insert id="insertUser" parameterType="com.mybatis.entity.User" >
    <!-- 
        将insert插入的数据的主键返回到User对象中;
        select last_insert_id():得到刚insert进去记录的主键值,只适用于自增主键;
        keyProperty:将查询到的主键值,设置到parameterType指定的对象的那个属性
        order:select last_insert_id()执行顺序,相对于insert语句来说它的执行顺序。
        resultType:指定select last_insert_id()的结果类型;
     -->
    <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
        select last_insert_id()
    </selectKey>
    <!-- 
        使用mysql的uuid(),实现非自增主键的返回。
        执行过程:通过uuid()得到主键,将主键设置到user对象的Id的属性中,其次,在insert执行时,从user对象中取出Id属性值;
     <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
        select uuid()
    </selectKey>
        insert into t_user (id,username,birthday,sex,address) values(#{id},#{username},#{birthday},#{sex},#{address})
     -->
    insert into t_user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
<!-- 删除用户 
    根据ID删除用户,需要输入Id值
-->
    <delete id="deleteUser" parameterType="java.lang.Integer">
        delete from t_user where id=#{id}
    </delete>
<!-- 更新用户 
    需要传入用户的Id和用户的更新信息
    parameterType:指定User对象,包括Id和用户的更新信息,注意:Id是必须存在的
    #{id}:从输入的User对象中获取Id的属性值
-->
<update id="updateUser" parameterType="com.mybatis.entity.User">
    update t_user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} 
    where id=#{id}
</update>

</mapper>

五、基于注解映射实完整数据访问

1、定义映射器,StudentMapper接口

package com.mzj.mybatis.dao.annotation;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/12 12:49
 * @Version: 1.0
 */
import com.mzj.mybatis.entitles.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface StudentMapper {
    /**
     * 根据学生编号获得学生对象
     */
    @Select("select id,name,sex from student where id=#{id}")
    Student selectStudentById(int id);

    /**
     * 根据学生姓名获得学生集合
     */
    @Select("SELECT id,name,sex from student where name like '%${value}%'")
    List<Student> selectStudentsByName(String name);

    /**
     * 添加学生
     */
    @Insert("insert into student(name,sex) values(#{name},#{sex})")
    int insertStudent(Student entity);

    /**
     * 更新学生
     */
    @Update("update student set name=#{name},sex=#{sex} where id=#{id}")
    int updateStudent(Student entity);

    /**
     * 删除学生
     */
    @Delete("delete from student where id=#{id}")
    int deleteStudent(int id);
}

2、定义mybatis配置文件,其中mappers引入的是映射器,而基于XML的方式引入的是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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/world"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="mapper/studentMapper.xml"/>-->
        <mapper class="com.mzj.mybatis.dao.annotation.StudentMapper"></mapper>
    </mappers>
</configuration>

3、创建DAO类实现对student的数据访问

通常一个业务接口实现模式如下:

  • 创建session会话
  • 通过session获得某个业务映射器(之前定义的映射器接口,实现是mybatis创建的,获取到的并不是我们自己定义的DAO,这里DAO实现了映射器相同接口,是为了复用接口描述的行为特征,而实现是不同的两套)
  • 调用映射器的业务方法
  • 关闭session
package com.mzj.mybatis.dao.annotation;

/**
 * @Auther: mazhongjia
 * @Date: 2020/6/12 12:52
 * @Version: 1.0
 */
import com.mzj.mybatis.entitles.Student;
import com.mzj.mybatis.utils.SqlSessionFactoryUtil4Annotation;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDao4Annotation implements StudentMapper {

    /**
     * 根据学生编号获得学生对象
     */
    public Student selectStudentById(int id) {
        Student entity = null;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil4Annotation.openSession(true);

        //获得一个映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //查询单个对象,指定参数为3
        entity = mapper.selectStudentById(id);

        //关闭
        SqlSessionFactoryUtil4Annotation.closeSession(session);

        return entity;
    }


    /**
     * 根据学生姓名获得学生集合
     */
    public List<Student> selectStudentsByName(String name) {
        List<Student> entities = null;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil4Annotation.openSession(true);

        //获得一个映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //查询多个对象,指定参数
        entities =mapper.selectStudentsByName(name);
        //关闭
        SqlSessionFactoryUtil4Annotation.closeSession(session);
        return entities;
    }


    /**
     * 添加学生
     */
    public int insertStudent(Student entity) {
        //影响行数
        int rows=0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil4Annotation.openSession(true);

        //获得一个映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //执行添加
        rows = mapper.insertStudent(entity);
        //关闭
        SqlSessionFactoryUtil4Annotation.closeSession(session);
        return rows;
    }

    /**
     * 更新学生
     */
    public int updateStudent(Student entity) {
        //影响行数
        int rows=0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil4Annotation.openSession(true);

        //获得一个映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //执行更新
        rows =mapper.updateStudent(entity);
        //关闭
        SqlSessionFactoryUtil4Annotation.closeSession(session);
        return rows;
    }

    /**
     * 删除学生
     */
    public int deleteStudent(int id) {
        //影响行数
        int rows=0;
        //打开一个会话
        SqlSession session = SqlSessionFactoryUtil4Annotation.openSession(true);

        //获得一个映射器
        StudentMapper mapper=session.getMapper(StudentMapper.class);

        //执行删除
        rows = mapper.deleteStudent(id);
        //关闭
        SqlSessionFactoryUtil4Annotation.closeSession(session);
        return rows;
    }

}

4、编写测试用例

package com.mzj.mybatis.dao;

import com.mzj.mybatis.dao.annotation.StudentDao4Annotation;
import com.mzj.mybatis.dao.annotation.StudentMapper;
import com.mzj.mybatis.entitles.Student;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

/**
 * StudentDao Tester.
 *
 * @author <Authors name>
 * @version 1.0
 * @since <pre>09/26/2018</pre>
 */
public class StudentDao4AnnotationTest {

    StudentMapper dao;
    @Before
    public void before() throws Exception {
        dao=new StudentDao4Annotation();
    }

    @After
    public void after() throws Exception {
    }

    /**
     * Method: selectStudentById(int id)
     */
    @Test
    public void testSelectStudentById() throws Exception {
        Student entity=dao.selectStudentById(1);
        System.out.println(entity);
        Assert.assertNotNull(entity);
    }

    /**
     * Method: selectStudentsByName(String name)
     */
    @Test
    public void testSelectStudentsByName() throws Exception {
        List<Student> students=dao.selectStudentsByName("e");
        System.out.println(students);
        Assert.assertNotNull(students);
    }

    /**
     * Method: insertStudent
     */
    @Test
    public void testInsertStudent() throws Exception {
        Student entity=new Student();
        entity.setName("张小强");
        entity.setSex("boy");

        Assert.assertEquals(1,dao.insertStudent(entity));
    }

    /**
     * Method: updateStudent
     */
    @Test
    public void testUpdateStudent() throws Exception {
        Student entity=dao.selectStudentById(7);
        entity.setName("张美丽");
        entity.setSex("girl");

        Assert.assertEquals(1,dao.updateStudent(entity));
    }

    /**
     * Method: deleteStudent
     */
    @Test
    public void testDeleteStudent() throws Exception {
        Assert.assertEquals(1,dao.deleteStudent(7));
    }
}

六、说明与注意事项

6.1、parameterType和resultType的区别

parameterType:在映射文件中通过parameterType指定输入参数的类型。

resultType:在映射文件中通过resultType指定输出结果的类型

6.2、#{}和${}的区别

#{}

#{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型,pojo、hashmap;

如果接收简单类型,#{}中可以写成value或其它名称;

#{}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。使用#{}意味着使用的预编译的语句,即在使用jdbc时的preparedStatement,sql语句中如果存在参数则会使用?作占位符,我们知道这种方式可以防止sql注入,并且在使用#{}时形成的sql语句,已经带有引号,例,select? * from table1 where id=#{id}? 在调用这个语句时我们可以通过后台看到打印出的sql为:select * from table1 where id='2' 加入传的值为2.也就是说在组成sql语句的时候把参数默认为字符串。

${}

表示一个拼接符号,会引用sql注入,所以不建议使用${};

${}接收输入参数,类型可以是简单类型,pojo、hashmap;

如果接收简单类型,${}中只能写成value;

${}接收pojo对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属性值。

使用${}时的sql不会当做字符串处理,是什么就是什么,如上边的语句:select * from table1 where id=${id} 在调用这个语句时控制台打印的为:select * from table1 where id=2 ,假设传的参数值为2
从上边的介绍可以看出这两种方式的区别,我们最好是能用#{}则用它,因为它可以防止sql注入,且是预编译的,在需要原样输出时才使用${},如,
select * from ${tableName} order by ${id} 这里需要传入表名和按照哪个列进行排序 ,加入传入table1、id 则语句为:select * from table1 order by id
如果是使用#{} 则变成了select * from 'table1' order by 'id' 我们知道这样就不对了。

 

Mybatis中#与$的区别

1.两者都是动态的向sql语句中传入需要的参数

2.#传入的参数在SQL中显示为字符串

     eg:select id,name,age from student where id =#{id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id ='1'.

3.$传入的参数在SqL中直接显示为传入的值

    eg:select id,name,age from student where id =${id},当前端把id值1,传入到后台的时候,就相当于 select id,name,age from student where id = 1.

4.#可以防止SQL注入的风险(语句的拼接)

5.但是如果使用在order by 中就需要使用 $.

我觉得#与的区别最大在于:#{} 传入值时,sql解析时,参数是带引号的,而的区别最大在于:#{} 传入值时,sql解析时,参数是带引号的,而{}穿入值,sql解析时,参数是不带引号的。

一 : 理解mybatis中 $与#

    在mybatis中的$与#都是在sql中动态的传入参数。

    eg:select id,name,age from student where name=#{name}  这个name是动态的,可变的。当你传入什么样的值,就会根据你传入的值执行sql语句。

二:使用$与#

   #{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符 。

   ${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。

    传入一个不改变的字符串或者传入数据库字段(列名),例如要传入order by 后边的参数

     这种情况下必须使用${}。

综上,#{}方式一般用于传入字段值,并将该值作为字符串加到执行sql中,一定程度防止sql注入;

           ${}方式一般用于传入数据库对象,例如传入表名,不能防止sql注入,存在风险。

           模糊查询中,如果使用如下方式:select * from reason_detail where reason_en like '%${reason}%',此处只能使用$,使用#的话,反而会被解析为列,报错java.sql.SQLException: Column 'reason' not found

情况一:只用  #{}

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName=#{userName} and userPassword =#{userPassword};
  </select>

 结果:

 ==>  Preparing: select * from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

 结论:

    #{}会在预编译期,生成两个  ?,作为占位符。

 

情况二:一个用  ${} 一个用 #{}

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName=${userName} and userPassword =#{userPassword};
  </select>

  结果:

 ==>  Preparing: select * from USER where userName=mww and userPassword =?; 
 ==>  Parameters: 123(String)

  结论:很显然  ${}  是直接拼成字符串的 ,#{}  是生成  ?占位符的。

     而且 因为  userName:mww 是字符串,所以 这种写法显然也是错误的。

     会报出如下错误:

### Error querying database.  Cause: java.sql.SQLSyntaxErrorException: Unknown column 'mww' in 'where clause'

  所以正确的写法是这样的:为字符串字段加上单引号 ' '

<select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName='${userName}' and userPassword =#{userPassword};
</select>

  结果:

 ==>  Preparing: select * from USER where userName='mww' and userPassword =?; 
 ==>  Parameters: 123(String)
 <==  Total: 1

  结论:显然这种写法是正确的,从这里可以看出,预编译期   ${}  是直接把参数拼结到SQL中,

     运行时,就只传入了一个 #{} 修饰的参数。

     ${}的这种写法还有一个安全隐患,那就是 SQL注入。

 

情况三: ${}   SQL注入:

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select * from USER where userName='${userName}' and userPassword =#{userPassword};
  </select>

  结果:

 ==>  Preparing: select * from USER where userName='' OR 1=1 OR '' and userPassword =?; 
 ==>  Parameters: 65787682342367(String)
 <==  Total: 2

  结论:只要我们在  ${}  输入   ' OR 1=1 OR '   无论后面的密码输入什么都可以,查询到数据,这种情况就是SQL注入。

 

 情况四:#{}  防止SQL注入

  <select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User">
        select * from USER where userName=#{userName} and userPassword =#{userPassword};
  </select>

  结果:

 ==>  Preparing: select * from USER where userName=? and userPassword =?; 
 ==>  Parameters: ' OR 1=1 OR '(String), 123(String)
 <==  Total: 0

  结论:上面预编译SQL的参数已经由占位符 { ?} 代替,所以传入的  ' OR 1=1 OR '  只会作为  userName字段的值,

     而不会拼入执行的SQL。这样就达到了防止SQL注入的目的。

 

在这种用法中, #{} 显然比 ${} 用法更好。

那 ${} 为什么经常在  Mybatis  使用那?

 

${}正确用法(个人见解):

  1、同时传入一个字段名和字段值:

User u=userService.getUserByNameAndPsw("userName,userType,userPassword",userName,userPassword);

    SQL: select ${arg0} from USER

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select ${arg0} from USER where userName=#{userName} and userPassword =#{userPassword};
  </select>

    结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

    结论:

      生成了我们想要SQL语句 :select userName,userType,userPassword from USER。。。。

 

   2、传入两个字段名和字段值:

User u=userService.getUserByNameAndPsw("userName,userType,userPassword",userName,userName,userPassword);

    SQL:  select ${arg0} from USER where ${arg1}=#{userName}

  <select id="getUserByNameAndPsw" resultType="com.hotel3.model.User">
        select ${arg0} from USER where ${arg1}=#{userName} and userPassword =#{userPassword};
  </select>

    结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

    结论:

      按照传参的顺序匹配 SQL 中的 ${arg0},${arg1}。。。

      生成我们想要的代码,但这个方式会使Mybatis 的 Mapper 文件可读性变差,

      如果不看其他的代码,很难辨别  ${arg0} ,${arg1} 代表的含义。

   3、使用Map传值,提高 Mapper 文件的可读性

     Map map =new HashMap();
        map.put("selectValues","userName,userType,userPassword");
        map.put("userNamefieId","userName");
        map.put("userName",userName);
        map.put("userPassword",userPassword);
        User u=userService.getUserByNameAndPsw(map);

 

    Mapper 文件的 xml

  <select id="getUserByNameAndPsw" parameterType="map" resultType="com.hotel3.model.User">
        select ${selectValues} from USER where ${userNamefieId}=#{userName} and userPassword =#{userPassword};
  </select>

    结果:

 ==>  Preparing: select userName,userType,userPassword from USER where userName=? and userPassword =?; 
 ==>  Parameters: mww(String), 123(String)
 <==  Total: 1

    结论:

      结果和arg0、arg1的传值方式相同,但 Mapper 文件的 xml 中的SQL语句可以

      通过对 map 中 key 值命名提高可读性

 

  注:以上SQL,均为预编期生成的SQL。

  注2:${} 每次传值不同 SQL 语句不同,可以灵活生成 SQL, 

     但每次都需要进行预编译,对效率会有影响,至于要不要使用 ${}还要具体情况具体分析。

6.3、selectOne()和selectList()的区别

selectOne表示查询出一条记录进行映射。如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象),如果查询结果为多条则会报错。

selectList表示查询出一个列表(多条记录)进行映射,可以是0到n条记录返回。

6.4、映射器选择XML还是注解

以下是MyBatis官网对Mapper Annotations的解释:

Mapper Annotations

Since the very beginning, MyBatis has been an XML driven framework. The configuration is XML based, and the Mapped Statements are defined in XML. With MyBatis 3, there are new options available. MyBatis 3 builds on top of a comprehensive and powerful Java based Configuration API. This Configuration API is the foundation for the XML based MyBatis configuration, as well as the new Annotation based configuration. Annotations offer a simple way to implement simple mapped statements without introducing a lot of overhead.

NOTE : Java Annotations are unfortunately limited in their expressiveness and flexibility. Despite a lot of time spent in investigation, design and trials, the most powerful MyBatis mappings simply cannot be built with Annotations – without getting ridiculous that is. C# Attributes (for example) do not suffer from these limitations, and thus MyBatis.NET will enjoy a much richer alternative to XML. That said, the Java Annotation based configuration is not without its benefits.

翻译:

(最初MyBatis是基于XML驱动的框架。MyBatis的配置是基于XML的,语句映射也是用XML定义的。对于MyBatis3,有了新的可选方案。MyBatis3 是建立在全面且强大的Java配置API之上的。 该配置API是MyBatis基于XML配置的基础,也是基于注解配置的基础。注解提供了简单的方式去实现简单的映射语句,不需要花费大量的开销。

注意:很不幸的是,java注解在表现和灵活性上存在限制。虽然在调研、设计和测试上花费了很多时间,但是最强大的MyBatis映射功能却无法用注解实现。这没有什么可笑的。举例来说,C#的特性就没有这个限制,所以MyBatis.NET 能拥有一个功能丰富的多的XML替代方案。所以,Java基于注解的配置是依赖于其自身特性的。)

长远来看建议选择XML作为映射器

http://www.mybatis.org/mybatis-3/java-api.html

七、视频

https://www.bilibili.com/video/av32447485/

八、示例

https://git.coding.net/zhangguo5/MyBatis02.git

九、作业

1、请使用MyBatis完成一个用户管理的数据访问功能,要求实现根据用户名查询用户对象(id,username,password,name,email,state)功能,表中至少5个字段。

2、请分别使用XML与注解两种方式实现对象用户表(Users)的单条记录查询、多条记录查询、增加、修改与删除功能,要求单元测试通过。

3、添加用户成功后返回用户的编号,而不是影响行数。(选作)

4、实现多个条件组合查询,类似在电商平台购物可以选择0-n个条件,且可以自由组合。(选作)

5、实现分页功能。(选作)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值