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

Mybatis入门与Mapper动态代理

程序员文章站 2022-06-17 15:23:27
...

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)配置起来,并通过j ava 对象和 statement 中的 sql 进行映射生成最终执行的 sql 语句,最后由 Mybatis 框架执行 sql 并将结果映射成 java 对象并返回。

Mybatis 架构

Mybatis入门与Mapper动态代理
架构图详解:

1、mybatis配置
  SqlMapConfig.xml,此文件作为mybatis的全局配置文件,配置了mybatis的运行环境等信息。
  mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加   载。
2、通过mybatis环境等配置信息构造SqlSessionFactory即会话工厂
3、由会话工厂创建sqlSession即会话,操作数据库需要通过sqlSession进行。
4、mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,
  一个是基本执行器、一个是缓存执行器。
5Mapped Statement也是mybatis一个底层封装对象,它包装了mybatis配置信息及sql映射信息等。
  mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
6Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,
  Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对preparedStatement设置参数。
7Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,
  Executor通过MappedStatement在执行sql后将输出结果映射至java对象中,输出结果映射过程相当于jdbc编程中对结果的解析处理过程。

Mybatis 入门

我们先来写一个入门程序,来了解一下 Mybatis 具体的使用流程,具体的细节会在后面一一讲解

第一步:创建数据库和数据表

CREATE DATABASE mybatis; 
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=27 DEFAULT CHARSET=utf8;


INSERT INTO `user` VALUES ('1', '王五', NULL, '2', NULL);
INSERT INTO `user` VALUES ('10', '张三', '2014-07-10', '1', '广东深圳');
INSERT INTO `user` VALUES ('16', '张小明', NULL, '1', '广东广州');
INSERT INTO `user` VALUES ('22', '陈小明', NULL, '1', '广东广州');
INSERT INTO `user` VALUES ('24', '张三丰', NULL, '1', '广东广州');
INSERT INTO `user` VALUES ('25', '陈小明', NULL, '1', '广东广州');
INSERT INTO `user` VALUES ('26', '王五', NULL, NULL, NULL);

第二步:创建 java 项目,导入 DTD 约束文件和jar包,配置 Mybatis 核心配置文件sqlMapConfig.xml ,SqlMapConfig.xml 是配置文件内容为数据源、事务管理。(配置文件写在 src 根目录下即可,或者也可以在 src下创建一个文件夹 config,用于统一存放配置文件)
Mybatis入门与Mapper动态代理

<?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>
    <typeAliases>
        <package name="com.pngyul.mybatis.pojo" />
    </typeAliases>
    <!-- 和spring整合后 environments配置将废除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理 -->
            <transactionManager type="JDBC" />
            <!-- 数据库连接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url"
                    value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments>
    <!--要引入映射文件 mapper.xml,该文件名字可以随意,但最好与你对应的数据库表名一致
        该User0.xml文件在第四步会写
      -->
    <mappers>
        <mapper resource="User0.xml" />
    </mappers>
</configuration>

第三步:创建和数据库表对应的 pojo 类

public class User implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private Integer id;
    private String username;// 用户姓名
    private String sex;// 性别
    private Date birthday;// 生日
    private String address;// 地址


    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", sex=" + sex
                + ", birthday=" + birthday + ", address=" + address + "]";
    }
}

第四步:创建 sql 映射文件 Use0.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="test"> 
    <!--id为sql语句的id,parameterType为参数类型,resultType为结果类型,#{}为占位符-->
    <!--根据id查找用户,#{}里面可以随便写-->
    <select id="findUserById" parameterType="Integer" resultType="com.pngyul.mybatis.pojo.User">
        select * from user where id =#{id}
    </select>
</mapper>

第五步:测试程序

@Test
public void findUserById() throws IOException {
    // 加载核心配置文件
    String resource = "sqlMapConfig.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    // 创建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    // 创建SqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 执行Sql语句
    User user = sqlSession.selectOne("test.findUserById", 10);
    System.out.println(user);
    }

模糊查询

<!-- 模糊查询 -->
<select id="findUserByUsername" parameterType="String" resultType="com.pngyul.mybatis.pojo.User">
        select * from user where username like "%"#{haha}"%"
</select>
<!--sql 语句还有另一种写法-->
<!-- ${}里面只能是value -->
select * from user where username like '%${value}%'
//根据用户名称模糊查询用户列表
@Test
public void findUserByUsername() throws Exception {
    String resource = "sqlMapConfig.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    List<User> users = sqlSession.selectList("test.findUserByUsername", "五");
    for (User user2 : users) {
        System.out.println(user2);  
    }
}

添加

<!-- 添加操作 -->
<insert id="insertUser" parameterType="com.pngyul.mybatis.pojo.User">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
 @Test
 public void test() throws IOException {
      String resource="sqlMapConfig.xml";
      InputStream in=Resources.getResourceAsStream(resource);
      SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
      SqlSession sqlSession=factory.openSession();  
      User user=new User();
      user.setUsername("pngyul");
      user.setBirthday(new Date()); 
      user.setSex("男");
      user.setAddress("广东");
      sqlSession.insert("test.insertUser", user);
      sqlSession.commit();
      sqlSession.close();
 }

添加并返回数据库自增主键

<!-- 保存用户 -->
<insert id="saveUser" parameterType="com.pngyul.mybatis.pojo.User">
    <!-- selectKey 标签实现主键返回 -->
    <!-- keyColumn:主键对应的表中的哪一列 -->
    <!-- keyProperty:主键对应的pojo中的哪一个属性 -->
    <!-- order:设置在执行insert语句前执行查询id的sql,还是在执行insert语句之后执行查询id的sql -->
    <!-- resultType:设置返回的id的类型 -->
    <!--LAST_INSERT_ID():是mysql的函数,返回auto_increment自增列新记录id值。-->
    <selectKey keyColumn="id" keyProperty="id" order="AFTER"
        resultType="Integer">
        SELECT LAST_INSERT_ID()
    </selectKey>
    INSERT INTO `user`
    (username,birthday,sex,address) VALUES
    (#{username},#{birthday},#{sex},#{address})
</insert
@Test
public void insertUser() throws Exception {
    String resource = "sqlMapConfig.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = new User();
    user.setAddress("广东");
    user.setBirthday(new Date());
    user.setSex("男");
    user.setUsername("ccfff");
    int i = sqlSession.update("test.insertUser", user);
    sqlSession.commit();
    System.err.println(user.getId());
    sqlSession.close();
    }

修改

<!-- 修改操作 -->
</update>
<delete id="deleteUserById" parameterType="Integer">
    delete from user where  id=#{id}
</delete>
@Test
public void updateUserById() throws Exception {
    String resource = "sqlMapConfig.xml";
    InputStream in = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = new User();
    user.setAddress("广东");
    user.setBirthday(new Date());
    user.setSex("女");
    user.setUsername("ccfff");
    user.setId(30);
    int i = sqlSession.update("test.updateUserById", user);
    sqlSession.commit();
    sqlSession.close();
}

删除

<!-- 删除操作 -->
<delete id="deleteUser" parameterType="Integer">
    delete from user where id=#{id}
</delete>
@Test
public void test() throws IOException {
       String resource="sqlMapConfig.xml";
       InputStream in=Resources.getResourceAsStream(resource);
       SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
       SqlSessionFactory factory=builder.build(in);
       SqlSession sqlSession=factory.openSession();  
       sqlSession.delete("deleteUser", 27);
       sqlSession.commit();
       sqlSession.close();
    } 

原始 Dao 开发方法

简单举个例子,了解一下即可

/1、编写mapper文件,并引入到核心配置文件中
<select id="findUserById" parameterType="Integer"
        resultType="User">
        select * from user where id = #{v}
</select>
//2、定义一个UserDao接口
public interface UserDao {
    User queryUserById(Integer id);
}
//3、UserDao实现类
package com.pngyul.mybatis.dao;

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

import com.pngyul.mybatis.pojo.User;

public class UserDaoImpl implements UserDao {
    private SqlSessionFactory sqlSessionFactory;


    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        super();
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public User queryUserById(Integer id) {
        SqlSession sqlsession = sqlSessionFactory.openSession();
        User user = sqlsession.selectOne("test.findUserById", id);
        sqlsession.close();
        return user;
    }
}
//测试
public class Junit {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws Exception {
        // 创建SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 加载SqlMapConfig.xml配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlsessionFactory
        this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void testQueryUserById() {
        // 创建DAO
        UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
        // 执行查询
        User user = userDao.queryUserById(10);
        System.out.println(user);
    }

Mapper 动态代理方式

Mapper接口开发方法只需要程序员编写Mapper接口(相当于 Dao 接口),由 Mybatis 框架根据接口定义创建接口的动态代理对象。

Mapper接口开发需要遵循以下四个规范:

  • Mapper.xml 文件中的 namespace 与 mapper 接口的类路径相同,否则会找不到接口,也就无法生成代理对象。
  • Mapper 接口方法名和 Mapper.xml 中定义的每个方法的 id 相同
  • Mapper接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同
  • Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同

演示 Mapper 动态代理方式步骤:

1、编写Mapper文件 

<?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.pngyul.mybatis.mapper.UserMapper">
    <select id="findUserById" parameterType="int" resultType="com.pngyul.mybatis.pojo.User">
        select * from user where id =#{id}
    </select>

    <!-- 模糊查询 -->
    <select id="findUserByUsername" parameterType="String" resultType="com.pngyul.mybatis.mapper.UserMapper">
        <!-- ${}里面只能是value -->
        select * from user where username like '%${value}%'
    </select> 

    <!-- 添加操作 -->
    <insert id="insertUser" parameterType="com.pngyul.mybatis.mapper.UserMapper">
        insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>

    <!-- 修改操作 -->
    <update id="updateUser" parameterType="com.pngyul.mybatis.mapper.UserMapper">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
    </update>

    <!-- 删除操作 -->
    <delete id="deleteUser" parameterType="Integer">
        delete from user where id=#{id}
    </delete>
</mapper>
2、创建Mapper接口 

public interface UserMapper {
    public User findUserById(int id);
    public List<User> findUserByUsername(String username); 
    public void insertUser(User user); 
    public void deleteUser(Integer id);
}
3、核心配置文件中引入映射文件 

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
4、测试  
public class MybatisTest {

    @Test
    public void test() throws IOException {
        String resource="sqlMapConfig.xml";
        InputStream in=Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
        SqlSessionFactory factory=builder.build(in);
        SqlSession sqlSession=factory.openSession();  
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.findUserById(1);
        System.out.println(user);
    } 
}

开发中使用这种方式来进行开发,简便快捷,代码复用性高,免去很多重复繁琐代码。

Mybatis 的 SqlMapConfig.xml 文件说明

MyBatis 的配置文件包含了 MyBatis 的设置和属性信息。文档的结构和顺序如下:

properties 属性
settings 设置
typeAliases 类型命名
typeHandlers 类型处理器
objectFactory 对象工厂
plugins 插件
environments(环境集合属性对象)
    environment(环境子属性对象)
        transactionManager(事务管理)
        dataSource(数据源)
databaseIdProvider 数据库厂商标识
mappers 映射器 

下面重点介绍几个:

properties(属性)

//db.peoperties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
//引入文件
<properties resource="db.properties"/>
<dataSource type="POOLED">
     <property name="driver" value="${jdbc.driver}" />
     <property name="url" value="${jdbc.url}" />
     <property name="username" value="${jdbc.username}" />
     <property name="password" value="${jdbc.password}" />
</dataSource>

如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。

typeAliases(类型别名)

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

<typeAliases>
     <!-- 单个别名定义 -->
     <typeAlias alias="user" type="cn.cad.pojo.User" /> 
     <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) -->
     <package name="cn.itcast.mybatis.pojo" />
     <package name="其它包" />
</typeAliases>

在mapper文件中就可以使用别名user。

Mybatis 已经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,例如 string,integer 等。

mappers(映射器)

映射器配置会告诉了 MyBatis 去哪里找映射文件。

<mapper resource=" " />:使用相对于类路径的资源(现在的使用方式)

<mapper url="file:///var/mappers/AuthorMapper.xml"/>:使用绝对路径,基本不会用 

<mapper class="cn.itcast.mybatis.mapper.UserMapper"/>:使用mapper接口类路径。注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。 

<package name="xxx"/>:注册指定包下的所有mapper接口,此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

PS::部分学习资料源于传智播客