MyBatis 由浅入深(实践篇)-2

MyBatis 开发 DAO(Data Access Object)

两种思路:

  • 原始 Dao 开发方法(分别编写 Dao 接口 和 接口实现类 DaoImpl)
  • 借助 MyBatis,使用 mapper 接口(相当于Dao 接口)代理开发

预期功能

使用 Dao接口 实现 单表 的 CRUD

示例程序

原始 Dao 开发

思路:
设计Dao 接口,在 Dao 实现类中注入 SqlsessionFactory 在方法体中通过 SqlsessionFactory 创建 Sqlsession 执行对应的 sql

  1. 设计Dao 接口
import cn.guan.mybatis.example2.User;

/**
 * User Dao 接口定义
 * @Created 2017/9/7.
 */
public interface UserDao {

    //根据id查询用户信息
    User findUserById(int id) throws Exception;

    //添加用户信息
    void addUser(User user) throws Exception;

    //删除用户信息
    void deleteUser(int id) throws Exception;

}
  1. Dao 接口实现
import cn.guan.mybatis.example2.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

/**
 * User Dao 实现类
 * @Created 2017/9/7.
 */
public class UserDaoImpl implements UserDao{

    //需要在 Dao 实现类中注入 SqlsessionFactory
    //这里通过构造方法注入
    private SqlSessionFactory sqlSessionFactory;

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

    @Override
    public User findUserById(int id) throws Exception {
        //在方法体内通过 SqlsessionFactory 创建 Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        sqlSession.close();
        return user;
    }

    @Override
    public void addUser(User user) throws Exception {
        //在方法体内通过 SqlsessionFactory 创建 Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行插入的操作
        sqlSession.insert("test.insetrUser", user);
        //提交事务
        sqlSession.commit();
        //释放资源
        sqlSession.close();
    }

    @Override
    public void deleteUser(int id) throws Exception {
        //在方法体内通过 SqlsessionFactory 创建 Sqlsession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.delete("test.deleteUserById", id);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
}
  1. 功能测试
import cn.guan.mybatis.example2.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.text.SimpleDateFormat;

/**
 * 原始 dao 测试
 * @Created 2017/9/7.
 */
public class UserDaoImplTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws Exception
    {
        //创建sqlSessionFactory
        //Mybatis 配置文件
        String resource = "mybatis-config.xml";
        //得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂,传入Mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testFindUserById() throws Exception
    {
        //创建UserDao的对象
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        //调用UserDao方法
        User user = userDao.findUserById(1);
        System.out.println(user);
    }

    @Test
    public void testAddUser(){
        //创建UserDao的对象
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        try {
            User user = new User();
            user.setUsername("赵六");
            //为了设置生日的日期输入
            SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd");
            user.setBirthday(sdf.parse("2005-01-12"));
            user.setSex("男");
            user.setAddress("湖南长沙");

            userDao.addUser(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testDeleteUser(){
        //创建UserDao的对象
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        try {
            userDao.deleteUser(3);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

  1. 思考
    原始Dao开发中存在一些问题,如下
  1. Dao方法体存在重复代码:通过 SqlSessionFactory 创建 SqlSession,调用 SqlSession 的数据库操作方法
  2. 调用 sqlSession 的数据库操作方法需要指定 statement 的id,这里存在硬编码,不得于开发维护
  3. 调用 sqlSession 的数据库操作方法时传入的变量,由于 sqlsession 方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

使用 Mybatis 的 mapper 接口开发 Dao

思路:
编写 *mapper.xml 映射文件,设计 mapper 接口(相当于 Dao 接口),遵循 MyBatis mapper 接口开发规范,框架自动生成 mapper 接口类的代理对象,自动调用 *mapper.xml 中对应的 sql

mapper 接口开发规范:

  1. 在 *mapper.xml 中定义 namespace 等于 mapper 接口地址
  2. 在 mapper 接口(Dao 接口)中的方法名要与 *mapper.xml 中 statement 的 id 一致。
  3. 在 mapper 接口(Dao 接口)中的输入参数类型要与 *mapper.xml 中 parameterType 指定的参数类型一致。
  4. 在 mapper 接口(Dao 接口)中的返回值类型要与 *mapper.xml 中 resultType 指定的类型一致。

实现:

  1. 设计 mapper 接口
import cn.guan.mybatis.example2.User;

import java.util.List;

/**
 * User Mapper 接口定义
 * @Created 2017/9/7.
 */
public interface UserMapper {

    //根据id查询用户信息
    User findUserById(int id) throws Exception;

    //根据id查询用户信息
    List findUserByUsername(String username) throws Exception;

    //添加用户信息
    void insertUser(User user) throws Exception;

    //删除用户信息
    void deleteUserById(int id) throws Exception;

}
  1. 编写 *mapper.xml 并添加到全局配置文件(mybatis-config.xml)中

user-mapper.xml 编写:







    
    

    

    
    
        
            select LAST_INSERT_ID()
        
        insert into tb_user(username, birthday, sex, address)
        values(#{username}, #{birthday}, #{sex}, #{address})
    

    
    
        delete from tb_user where id = #{id}
    

    
    
        update tb_user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where id = #{id}
    


user-mapper.xml 配置到全局配置 mybatis-config.xml 中,项目运行时加载 user-mapper.xml




    
    
        
            
            
            
            
                
                
                
                
            
        
    

    
    
        
    

  1. 功能测试
import cn.guan.mybatis.example2.User;
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.Before;
import org.junit.Test;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.List;

/**
 * 测试 MyBatis mapper 接口实现 Dao 
 * @Created 2017/9/7.
 */
public class UserMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws Exception
    {
        //创建sqlSessionFactory
        //Mybatis 配置文件
        String resource = "mybatis-config.xml";
        //得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //创建会话工厂,传入Mybatis的配置文件信息
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testFindUserById() throws Exception
    {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper的方法
        User user = userMapper.findUserById(4);
        System.out.println(user);
    }

    @Test
    public void testFindUserByUsername(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //创建usermapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用UserMapper的方法
        try {
            List users = userMapper.findUserByUsername("赵");
            System.out.println(users == null ? "[null]":users.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testAddUser(){
        //设置 sqlSession 开启自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //创建usermapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        try {
            User user = new User();
            user.setUsername("赵六一");
            //为了设置生日的日期输入
            SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd");
            user.setBirthday(sdf.parse("2001-01-12"));
            user.setSex("男");
            user.setAddress("湖南岳阳");

            userMapper.insertUser(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testDeleteUser(){
        //设置 sqlSession 开启自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //创建usermapper对象,mybatis自动生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        try {
            userMapper.deleteUserById(7);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

  1. 思考

mapper 解决的问题:
遵循 mapper 接口开发规范,就会由 MyBatis 对 SqlSession 接口方法的调用进行统一生成

sqlSession.selectOne()
sqlSession.selectList()
sqlSession.insert()
sqlSession.delete()
sqlSession.update()
···
  1. 注意事项

mapper 接口方法参数只能有一个

MyBatis sql 执行过程分析

面向用户的关键对象分析

  1. SqlSessionFactoryBuilder
    SqlSessionFactoryBuilder 用于创建 SqlSessionFacoty,SqlSessionFacoty 一旦创建完成就不需要SqlSessionFactoryBuilder 了,因为 SqlSession 是通过 SqlSessionFactory 生产,所以可以将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。
  2. SqlSessionFactory
    SqlSessionFactory 是一个接口,接口中定义了 openSession 的不同重载方法,SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理 SqlSessionFactory。
  3. SqlSession
    SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。
    SqlSession 是通过 SqlSessionFactory 创建 ,而 SqlSessionFactory 是通过 SqlSessionFactoryBuilder 进行创建。
    SqlSession 是一个面向用户的接口, sqlSession 中定义了数据库操作,默认使用 DefaultSqlSession 实现类。

SQL 执行过程

  1. 加载配置信息(mybatis-config.xml、*mapper.xml)

mybatis-config.xml 全局配置

  • 数据源信息

    
    
        
        
        
        
            
            ···
               
    

  • mapper 接口 与 sql 的映射文件地址


    
    ···

*mapper.xml 中为 mapper 接口对应 sql 定义,参数、返回数据与 java 对象的映射关系