zoukankan      html  css  js  c++  java
  • Mybatis(一)——HelloWorld

    本人的博客一向保持"傻瓜式"的风格。

    循序渐进学Mybatis,先konw how,再konw why。先整体,再细节!

    本文不讲难懂的概念,先通过一个案例,希望读者跟着本文一步一步的实现,再探究中间细节及原理。

    相信只要认真阅读本文,一定会带给你收获!

    一、Mybatis介绍

      MyBatis 是一款优秀的持久层框架,它支持定制化 SQL存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。

      官网简介,好了,废话不多说。下面通过一个案例,学习如何使用 Mybatis 把数据库中一条记录查询出来并封装成一个普通的 Java 对象。

    二、快速入门

    1、下载jar包

      【Mybatis开发包】mybatis-3.4.1.jar

      【MySQL驱动包】mysql-connector-java-5.1.39-bin.jar

      做 Mybatis 开发,自然需要一些 Mybatis 的开发包。在 官网:http://www.mybatis.org/mybatis-3/ 中可以下载到。打开后,将网页语言选择简体中文(这里也有 Mybatis 的官网开发文档。个人觉得想提高技术的需要看文档),可以看到 Mybatis 目前所有的版本,本文采用 Mybatis-3.4.1 的版本做案例学习。下载好 mybatis-3.4.1.zip (开发包) 和 源代码(zip)(源码包)以便后面使用。mybatis-3.4.1.zip解压就有mybatis-3.4.1.jar。mysql-connector-java-5.1.39-bin.jar的获取本文就不做阐述了。

     

    2、创建数据库和表

      本文采用MySQL数据库,由于需要从数据库中查询出一条记录封装到 Java 对象中,这里事先创建一个数据库,名为 mybatis ,创建一张表,名为 user ,表结构和数据如图所示。注意:红框处密码字段是 pass_word。

     

    3、创建实体类

       打开 eclipse ,新建一个 Java Project , 创建一个实体类(User.java)用于封装数据,属性与数据库中的字段一一对应。

      User类代码如下。注意:此处密码属性是passWord。后面会作解释。

     1 package com.originator.model;
     2 
     3 public class User {
     4 
     5     private Integer id; // id号
     6     private String name; // 用户名
     7     private String passWord; // 密码
     8     private String email; // 邮箱
     9     private String gender; // 性别
    10 
    11     public Integer getId() {
    12         return id;
    13     }
    14 
    15     // get & set
    16     public void setId(Integer id) {
    17         this.id = id;
    18     }
    19 
    20     public String getName() {
    21         return name;
    22     }
    23 
    24     public void setName(String name) {
    25         this.name = name;
    26     }
    27 
    28     public String getPassWord() {
    29         return passWord;
    30     }
    31 
    32     public void setPassWord(String passWord) {
    33         this.passWord = passWord;
    34     }
    35 
    36     public String getEmail() {
    37         return email;
    38     }
    39 
    40     public void setEmail(String email) {
    41         this.email = email;
    42     }
    43 
    44     public String getGender() {
    45         return gender;
    46     }
    47 
    48     public void setGender(String gender) {
    49         this.gender = gender;
    50     }
    51 
    52     @Override
    53     public String toString() {
    54         return "User [id=" + id + ", name=" + name + ", passWord=" + passWord + ", email=" + email + ", gender="
    55                 + gender + "]";
    56     }
    57 
    58 }

    4、引入jar包

      在项目中新建一个文件夹用于存放 jar 包。右键—>new—>Folder,取名 lib 。将之前下载好的【Mybatis开发包】、【MySQL驱动包】复制到 lib 目录下,并选中后添加引入到项目中。右键—>Build Path—>Add to Build Path。

     

    5、创建 mybatis 的全局配置文件

      由于实际开发中习惯将配置文件与 Java 文件分开,便于管理,所以新建一个资源文件。右键—>new—>Source Folder,取名resources。在 resources 目录下新建一个文件,右键—>new—>File,取名 mybatis-config.xml

      mybatis-config.xml 文件内容如下:

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE configuration
     3   PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     4   "http://mybatis.org/dtd/mybatis-3-config.dtd">
     5 <configuration>
     6     <environments default="development">
     7         <environment id="development">
     8             <transactionManager type="JDBC" />
     9             <dataSource type="POOLED">
    10                 <property name="driver" value="com.mysql.jdbc.Driver" />
    11                 <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
    12                 <property name="username" value="root" />
    13                 <property name="password" value="123456" />
    14             </dataSource>
    15         </environment>
    16     </environments>
    17     <mappers>
    18         <mapper resource="org/mybatis/example/BlogMapper.xml" />
    19     </mappers>
    20 </configuration>
    21 
    22 mybatis-config.xml

      这里,其他标签都先不管,读者不需要理会,先照抄即可。只需看到 dataSource 标签下有4个属性标签 property ,name 是键,value是值。

      name = "driver",表示数据库驱动,值是固定的,"com.mysql.jdbc.Driver" 不需要修改。

      name = "url",表示数据库连接的url,值前面是 mysql 的前缀,localhost 表示本地电脑,3306是数据库默认的端口号,mybatis表示要连接的数据库名。由于本文之前创建数据库的时候数据库名称即为mybatis,所以这里写mybatis,读者需要根据自己的数据库所在位置修改相应的片段。若连接远程电脑数据库,需要把 localhost 修改为远程电脑的 ip 地址;若安装的数据库没有使用默认的端口号,需要把3306修改为数据库使用的端口号;把 mybatis 修改为自己的数据库名。

      name = "username",表示数据库的用户名,这里读者将值修改为自己的即可。

      name = "password",表示数据库的密码,这里读者将值修改为自己的即可。

    6、编写测试类

      我们来创建一个测试类 UserTest.java。

      UserTest.java 文件内容如下:

     1 package com.originator.test;
     2 
     3 import java.io.IOException;
     4 import java.io.InputStream;
     5 
     6 import org.apache.ibatis.io.Resources;
     7 import org.apache.ibatis.session.SqlSession;
     8 import org.apache.ibatis.session.SqlSessionFactory;
     9 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    10 import org.junit.Test;
    11 
    12 public class UserTest {
    13 
    14     @Test
    15     public void test() throws IOException {
    16         String resource = "mybatis-config.xml";
    17         InputStream inputStream = Resources.getResourceAsStream(resource);
    18         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    19 
    20         SqlSession sqlSession = sqlSessionFactory.openSession();
    21         sqlSession.selectOne(statement, parameter);
    22     }
    23 }

       这里,方法体的注解 @Test 表示这是一个 JUnit 单元测试,读者在编写时可能会显示红色下划波浪线错误,如图。这时,将鼠标停留在注解上,eclipse 会帮我们自动识别这个错误并提示解决方法,这里点击 Add JUnit 4 library to the build path 即可自动导入Junit 4的相关 jar 包。若读者不会使用 JUnit 单元测试,那么把方法修改为 public static void main(String args[]){} 写成 main 方法,使其可以单独运行即可。

      此时,可以看到,UserTest.java依然报错,原因是 openSession.selectOne(statement parameter); 的两个参数statementparameter没有正确传递。不慌,下面一一解答。这里不对其他代码做过多解释,读者照抄即可

      若前面读者下载了 Mybatis 的源码包,并加入到项目 Build Path 中,可以查看 selectOne(statement, parameter方法;若读者没有下载也无所谓,本文下面给出源码中这两个参数的注释,

      * @param statement Unique identifier matching the statement to use.
      * @param parameter A parameter object to pass to the statement.

      statement:sql的唯一标识

      parameter:执行 sql 用到的参数

      什么意思呢?似乎还是不太明白,没关系,读者继续按照下面的步骤阅读自然会有解答。

    7、创建 mybatis 的映射文件

      在资源文件夹 resources 目录下新建一个文件,右键—>new—>File,取名 UserMapper.xml。 

      UserMapper.xml 文件内容如下:

    1 <?xml version="1.0" encoding="UTF-8" ?>
    2 <!DOCTYPE mapper
    3   PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4   "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5 <mapper namespace="haha">
    6     <select id="hehe" resultType="com.originator.model.User">
    7         select * from user where id = #{id}
    8     </select>
    9 </mapper>

      这里 1~4 行是 mybatis 映射文件的头,模板,照抄即可,读者可以先不理会。下面解释一个这个文件几个重要的地方。

      namespace = "haha"。名字空间,表示当前这个 mapper 标签名字,文本取名"haha"

      id = "hehe"。表示这个标签的唯一标识,标识名为"hehe"

      resultType = "com.originator.model.User"。表示查询的返回值类型。

      sql语句中 #{id},表示传递过来的参数中的id变量。

      不难理解:我们要查询一条 user 记录,那么它的 id 是多少呢?返回的是一条记录,如何封装成一个普通的 Java 对象呢?(这个 mybatis 帮我们做了,但我们必须指明返回值是什么类型?com.originator.model.User 就是创建的实体类的全类名,包名+类名

      那么问题来了?如何才能告诉程序,我们要执行这个标签的里面写的 sql 语句呢?

      这时,我们回到测试类 UserTest.java ,再来看看这两个参数。是不是有点明白什么了呢?

        statement:sql的唯一标识

        parameter:执行 sql 用到的参数

      是的,没错!

      statement 就是用来指明我要执行哪个文件(因为在项目中不止一个 XXXMapper.xml 文件)下的哪个sql标签(一个 XXXMapper.xml 中不止一个标签)。

      parameter 就是用来传递 sql 语句中的参数变量。

      改写 UserTest.java 文件内容如下:

     1 package com.originator.test;
     2 
     3 import java.io.IOException;
     4 import java.io.InputStream;
     5 
     6 import org.apache.ibatis.io.Resources;
     7 import org.apache.ibatis.session.SqlSession;
     8 import org.apache.ibatis.session.SqlSessionFactory;
     9 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    10 import org.junit.Test;
    11 
    12 import com.originator.model.User;
    13 
    14 public class UserTest {
    15 
    16     @Test
    17     public void test() throws IOException {
    18         String resource = "mybatis-config.xml";
    19         InputStream inputStream = Resources.getResourceAsStream(resource);
    20         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    21 
    22         SqlSession sqlSession = sqlSessionFactory.openSession();
    23         User user = sqlSession.selectOne("haha.hehe", 1);
    24 
    25         System.out.println(user.toString()); // 打印一下
    26     }
    27 }

      传递方式是名字空间 + 标签id的形式。查询一下 user 表中 id 为 1 的记录。所以参数是 "haha.hehe",1。这里参数一,读者在 UserMapper.xml 中取名什么,这里就相应的修改成什么。不过这两个地方(namespace 和 id)不可随意取名,以后会详解。

      最后我们打印一下查询出来的 user ,看是否查询成功。单元测试方式,测试类代码空白处,右键—>Run As—>JUnit Test。如果是 main 函数直接运行就可以了。结果报如下错误:

      Cause: java.io.IOException: Could not find resource org/mybatis/example/BlogMapper.xml

      不要慌,稍微看一下就知道,程序说它找不到这个路径 org/mybatis/example/ 下的 BlogMapper.xml 文件。

    8、将映射文件 UserMapper.xml 注册到全局配置文件 mybatis-config.xml

      这时,我们回到 mybatis-config.xml 。前文中只介绍了 dataSource 标签。下面介绍 mappers 标签,看标签名就知道,mapper:映射,s 表示复数,说明里面可以配很多个映射文件。<mapper resource="org/mybatis/example/BlogMapper.xml" />这里,我们并没有一个名为 BlogMapper.xml 的文件,所以刚刚程序才报错。我们要将我们自己的 mapper 文件注册到全局配置文件中。修改如下:

    1     <mappers>
    2         <mapper resource="UserMapper.xml" />
    3     </mappers>

      这里,UserMapper.xml 文件就在类路径下,所以直接写文件名。如果 UserMapper.xml 文件在 XXX 包下,即需要带上包名:XXX/UserMapper.xml 。

      再次运行单元测试。执行结果如下:

      可以看到,将数据库中 id = 1 的用户查询出来了。不过有个问题,为什么 passWord 没有值呢?这里填上前文挖的坑。修改 UserMapper.xml 中的sql语句如下:红框表示给数据库中查询出来的字段 pass_word 取别名 passWord。再运行一下单元测试。

      可以看到:这下终于程序正确执行,得到了我们想要的结果。读者是不是也有点感觉呢?没错,mybatis 就是通过字段名和属性名一致来进行映射的。但是一般在项目中数据库的设计字段都遵循 xxx_yyy_zzz。而类属性名遵循 xxxYyyZzz,总不可能每次写 sql 语句的时候都加个别名把,那好麻烦哦。别着急,这里先挖坑。后续慢慢介绍。

    三、面向接口式编程

      经过前面的案例,相信读者对 mybatis 的使用有了一定的了解,很多细节的地方可能还不太懂。本文也没有做解释,不过没关系,本文会在后续的章节中一一介绍。下面填一下前文挖的坑。

      前文中提到这两个地方(namespace 和 id)不可随意取名。

      不知读者是否有感觉到呢?执行 sql 语句的代码 openSession.selectOne(String statement, Object parameter) 这两个参数都是可以随意传的。

    1、原因

      参数一:statement 每次传递的时候,编程人员,还要去寻找一下,到底要执行哪个映射文件下的哪个 sql 语句。如果在项目中,XXXMapper.xml 文件很多,一个 XXXMapper.xml 文件中又有多个 sql 标签。在代码中,statement 参数就会特别繁琐,且很不利于维护。而且,如果一不小心,将 statement 写错了,在程序中,String 类型是不会报错的,只有在运行,执行时才会报错,这也很不利于代码检查。

      参数二:parameter 是一个 Object 类型,同样在调用 selectOne() 方式时,编程人员也无法知道,这个参数到底传什么。还要按照 statement 参数找到对应的 sql 语句,查看之后才知道。如果 sql 语句稍微复杂一点,一时半会不能一下知道参数传什么,这也很不利用开发。同样,对于 Object ,在传递时,无论传递什么,在程序中,也不会报错,开发者也是无法立即得知参数是否传递正确。简单的说,如果一个方法的传参是 int 型,而调用者传递了一个 String ,则编译器会立马报错,可以提示编程人员应该传什么。

    2、解决方案

      经过以上分析后,那么正确的方式应该怎样呢?不慌, mybatis 已经为了我们提供了 动态代理接口 的模式。如果读者不懂动态代理(Java 设计模式中的一种)也没有关系。只是一个名词而已,不要被吓到了。对于 Java 开发人员来说,接口,是我们很喜欢的一种方式,它只对调用者暴露了传递参数,以及得到一个什么样的返回值,而调用者不需要关心里面具体的实现。同样的,先请读者按照下面一步一步的实现。

      既然是面向接口式编程,那么我们先编写一个接口 UserMapper.java。放在包 com.originator.dao 下。这个主要是分层的概念,读者不必在意。

     

      UserMapper.java 文件内容如下:

    1 package com.originator.dao;
    2 
    3 import com.originator.model.User;
    4 
    5 public interface UserMapper {
    6 
    7     public User getUserById(Integer id);
    8 
    9 }

       很自然的可以写出这么一个接口。有一个方法是 public User getUserById(Integer id); 该方法就是通过传入一个 id 返回一个User对象。方法名取的有意义即可。细心的读者可能已经发现了,这个文件的名称和 UserMapper.xml 一样,只是后缀不一样。是的,因为他们是配套的关系(个人的理解)。一个 UserMapper.java 与 一个 UserMapper.xml 对应。(也有的人喜欢命名为 UserDao.java,其实是一回事),为什么说他们是配套的关系呢? 我们先使用一下这个接口。那么,怎么来用它呢?

       在 UserTest.java 测试类中,新增一个测试方法 test0(),内容如下:

     1     @Test
     2     public void test0() throws IOException {
     3         String resource = "mybatis-config.xml";
     4         InputStream inputStream = Resources.getResourceAsStream(resource);
     5         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
     6 
     7         SqlSession sqlSession = sqlSessionFactory.openSession();
     8         UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     9         User user = userMapper.getUserById(1);
    10 
    11         System.out.println(user.toString()); // 打印一下
    12     }

      这个 test0() 方法在 openSession 这里就与 test() 方法不一样了。它有一个方法叫 getMapper(Class<T> type),里面传入的是一个泛型,这里我们传入接口的类类型。返回的就是一个接口,然后用接口调用其方法的方式得到一个 User。

      光这样就行了吗?当然不行,现在只是获取到了这个接口,调用了方法,这个方法是去执行哪个 sql 语句呢?还需要修改 UserMapper.xml 的 namespace 和 id。内容如下:

      namespace:为接口的全类名。

      id:为方法的方法名。 

      再执行一下测试方法 test0() ,结果如下,正确。

      至此,面向接口式编程实现完成。下面对一些细节作解释,可能有的读者已经有点感觉了。一个接口,是不能被实例化的,那它调用方法不是应该报空指针异常吗?可是没有。那说明:在这句中,openSession.getMapper(UserMapper.class); mybatis 为我们创建了一个代理对象。有兴趣的读者可以打印一下返回得到的 userMapper,显示为 org.apache.ibatis.binding.MapperProxy@9629756。就说明它是一个代理的对象。而且,读者稍微思考一下,也能大概猜到,这个代理对象的方法大概做了什么。

      姑且对 openSession.getMapper(UserMapper.class) 简单理解如下:

      通过反射得到 UserMapper 的全类名。在代理对象调用 getUserById(1)时,获取到方法名,然后依然执行 sqlSession.selectOne("com.originator.dao.UserMapper.getUserById", 1);

      所以,在接口若定义别的方法,如 deleteById(Integer id); 在 xml 中就需要编写一个 sql 语句,其标签 id 为 deleteById。所以个人认为他们是配套的关系,在实际开发中,也习惯将其文件名保持一致,方便阅读,可以使开发者一眼就知道,哪个接口文件去找哪个 xml 文件。

    四、总结

      先对一些细节作解释。前文没有对 test() 方法中的代码写注释,也没有做过多的解释。读者可以简单理解一下:

      String resource = "mybatis-config.xml"; // 定义了 mybatis 全局配置文件的路径,然后通过 Java io流的方式读取了 xml 文件的内容,得到了一个 sqlSessionFactory (sql会话工厂),从工厂当中获取了一次会话(sqlSession),这是用于与数据库交互的,该对象中有很多方法,例如:

      insert(statement, parameter);

      delete(statement, parameter);

      update(statement, parameter);

      这里不一一介绍,相信读者自己也能明白。而代理模式前面也大概简单作了解释,这里不深入研究了(有兴趣的读者可以先了解一下 Java 的动态代理原理),在后续的篇幅中,会进一步做研究,包括现在一些细节的东西和挖的坑。

      在实际项目中,都是使用的 Mapper 接口开发,程序员只需要编写 Mapper 接口和 Mapper.xml。Mapper接口的编写需要遵守相关规范:

      mapper.xml中的名字空间 = Mapper接口类的全路径;
      mapper.xml中的 sql 标签 id = mapper接口类的方法名;

    作者:Craftsman-L

    本博客所有文章仅用于学习、研究和交流目的,版权归作者所有,欢迎非商业性质转载。

    如果本篇博客给您带来帮助,请作者喝杯咖啡吧!点击下面打赏,您的支持是我最大的动力!

  • 相关阅读:
    浅析NetFilter和iptables
    关于skb_header_pointer函数
    Linux kernel 绝对路径之d_path篇
    几个内核函数:filp_open、filp_read、IS_ERR、ERR_PTR、PTR_ERR
    一文读懂数字签名
    Nginx配置文件nginx.conf中文详解(总结)
    nginx 重写 rewrite 基础及实例
    最完美解决Nginx部署ThinkPHP项目的办法
    nginx中的try_files指令解释
    js电话号码正则校验--座机和手机号
  • 原文地址:https://www.cnblogs.com/originator/p/9711835.html
Copyright © 2011-2022 走看看