文档章节

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

 追梦汉子
发布于 2017/09/07 17:42
字数 2207
阅读 7
收藏 1

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

© 著作权归作者所有

共有 人打赏支持
粉丝 0
博文 3
码字总数 8997
作品 0
广州
程序员
学以致用——CSDN概要数据(积分、篇数、访问总量)的多元线性回归分析

“学以致用最根本的是要把理论的知识和实际的应用要联合起来,由浅入深地达到熟能生巧的目的,学到的东西要经常揣摩,真正地理解其含义(包括使用的方法,注意的事项)然后按照理论的要求在实...

hpdlzu80100
2017/12/29
0
0
泥沙砖瓦浆木匠/springboot-learning-example

springboot-learning-example spring boot 实践学习案例,是 spring boot 初学者及核心技术巩固的最佳实践。 推荐 springcloud-learning-example spring cloud 实践学习案例 https://github...

泥沙砖瓦浆木匠
2017/03/29
0
0
软件测试职业发展规划浅谈

很多刚刚迈入测试行业或已进入测试行业有一段时间的同学,对自己的发展路线仍然迷茫,不知道该如何在测试行业发展或者不知如何很好的规划自己的职业生涯。虽然前一段做过一次公开课,详细的介...

hblxp321
2014/06/17
0
0
系统的学习大数据分布式计算spark技术

我们在学习一门技术的时候一定要以系统的思维去学习,这样的话,不仅对你的提高有很大的帮助,也可以让你高效的使用这个技术。 对于学习spark,当然也是要以系统的思维去全面的学习。这篇博客...

tangweiqun
2017/09/24
0
0
Python学习资料篇

Python学习资料网络上比较多,看到好的资源,不及时记录下来,下次就找不到了,我把知乎上好的回到收藏汇总到这里,以便自己随时查看,包括Python入门书籍,网站,项目,官方文档,下载资源等...

BjarneCpp
2017/12/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

qduoj~前端~二次开发~打包docker镜像并上传到阿里云容器镜像仓库

上一篇文章https://my.oschina.net/finchxu/blog/1930017记录了怎么在本地修改前端,现在我要把我的修改添加到部署到本地的前端的docker容器中,然后打包这个容器成为一个本地镜像,然后把这...

虚拟世界的懒猫
35分钟前
1
0
UML中 的各种符号含义

Class Notation A class notation consists of three parts: Class Name The name of the class appears in the first partition. Class Attributes Attributes are shown in the second par......

hutaishi
46分钟前
0
0
20180818 上课截图

小丑鱼00
今天
1
0
Springsecurity之SecurityContextHolderStrategy

注:下面分析的版本是spring-security-4.2.x,源码的github地址是: https://github.com/spring-projects/spring-security/tree/4.2.x 先上一张图: 图1 SecurityContextHolderStrategy的三个......

汉斯-冯-拉特
今天
0
0
LNMP架构(Nginx负载均衡、ssl原理、生成ssl密钥对、Nginx配置ssl)

Nginx负载均衡 网站的访问量越来越大,服务器的服务模式也得进行相应的升级,比如分离出数据库服务器、分离出图片作为单独服务,这些是简单的数据的负载均衡,将压力分散到不同的机器上。有时...

蛋黄_Yolks
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部