MyBatis 由浅入深(实践篇)-2
MyBatis 由浅入深(实践篇)-2
追梦汉子 发表于9个月前
MyBatis 由浅入深(实践篇)-2
  • 发表于 9个月前
  • 阅读 7
  • 收藏 1
  • 点赞 0
  • 评论 0

移动开发云端新模式探索实践 >>>   

摘要: 比较 MyBatis 提供的 mapper 机制实现 Dao 与原始方法实现 Dao MyBatis 内部执行流程概述

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;

}

2.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();
    }
}

3.功能测试

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();
        }

    }

}

4.思考 原始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<User> findUserByUsername(String username) throws Exception;

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

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

}

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

user-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">

<!--namespace :命名空间,对 sql 进行分类化管理,用于隔离 sql 语句-->
<!--命名空间设置为 mapper 接口地址  -->
<mapper namespace="cn.guan.mybatis.example3.UserMapper">
    <!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="cn.guan.mybatis.example2.User">
        select * from tb_user where id = #{id}
    </select>

    <select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.guan.mybatis.example2.User">
        select * from tb_user where username like '%${value}%'
    </select>

    <!-- 添加用户 -->
    <insert id="insertUser" parameterType="cn.guan.mybatis.example2.User">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
        insert into tb_user(username, birthday, sex, address)
        values(#{username}, #{birthday}, #{sex}, #{address})
    </insert>

    <!--删除用户-->
    <delete id="deleteUserById" parameterType="int">
        delete from tb_user where id = #{id}
    </delete>

    <!--根据id更新用户-->
    <update id="updateUserById" parameterType="cn.guan.mybatis.example2.User">
        update tb_user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where id = #{id}
    </update>

</mapper>

user-mapper.xml 配置到全局配置 mybatis-config.xml 中,项目运行时加载 user-mapper.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>
    <!-- 和spring整合后 environments配置将废除-->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc事务管理,事务由 Mybatis 控制-->
            <transactionManager type="JDBC" />
            <!-- 数据库连接池,由Mybatis管理,db_mybatis,Mysql用户名root,123456 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/db_mybatis?characterEncoding=utf-8" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>

    <!--加载 mapper 文件 路径-->
    <mappers>
        <mapper resource="mapperDir2/user-mapper.xml" />
    </mappers>
</configuration>

3.功能测试

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<User> 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();
        }
    }

}

4.思考

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

sqlSession.selectOne()
sqlSession.selectList()
sqlSession.insert()
sqlSession.delete()
sqlSession.update()
···

5.注意事项

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 全局配置

  • 数据源信息
<environments ···>
	<!--数据源环境配置-->
	<environment ···>
		<!--事务管理器-->
		<transactionManager ··· />
		<!--数据源配置-->
		<dataSource ···>
			<property ··· />
			···
		</dataSource>		
	</environment>
</environments>
  • mapper 接口 与 sql 的映射文件地址
<!-- *mapper.xml 文件地址 -->
<mappers>
	<mapper ··· />
	···
</mappers>
  • *mapper.xml 中为 mapper 接口对应 sql 定义,参数、返回数据与 java 对象的映射关系
<mapper namespace="命名空间">
	<insert ···>
	<delete ···>
	<update ···>
	<select ···>
	<sql ···>
	···
</mapper>

通过查看 mybatis 的 config 文件的 dtd 文件 (http://mybatis.org/dtd/mybatis-3-mapper.dtd) 可知 mybatis-config.xml 中 configuration 下支持的标签如下所示

configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, >reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)

2.创建数据库链接
3.创建事务对象
4.创建 Executor (执行器)
5.SqlSession的实现类即 DefaultSqlSession,此对象中对操作数据库实质上用的是 Executor

注: 由于篇幅原因,这里只是简短介绍了执行流程,后面会详细分析

注意事项

SqlSession 的实例不能共享使用,它是线程不安全的,因此每个线程都应该有它自己的 SqlSession 实例。因此最佳的范围是请求或方法范围(定义局部变量使用)。绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。打开一个SqlSession;使用完毕就要关闭它。通常把这个关闭操作放到 finally 块中以确保每次都能执行关闭。

SqlSession session = sqlSessionFactory.openSession();
try {
     // do work
} finally {
    session.close();
}

参考链接: http://blog.csdn.net/column/details/13905.html

标签: MyBatis Java 框架
  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 0
博文 3
码字总数 8997
×
追梦汉子
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: