MyBatis 开发 DAO(Data Access Object)
两种思路:
- 原始 Dao 开发方法(分别编写 Dao 接口 和 接口实现类 DaoImpl)
- 借助 MyBatis,使用 mapper 接口(相当于Dao 接口)代理开发
预期功能
使用 Dao接口 实现 单表 的 CRUD
示例程序
原始 Dao 开发
思路:
设计Dao 接口,在 Dao 实现类中注入 SqlsessionFactory 在方法体中通过 SqlsessionFactory 创建 Sqlsession 执行对应的 sql
- 设计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;
}
- 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();
}
}
- 功能测试
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();
}
}
}
- 思考
原始Dao开发中存在一些问题,如下
- Dao方法体存在重复代码:通过 SqlSessionFactory 创建 SqlSession,调用 SqlSession 的数据库操作方法
- 调用 sqlSession 的数据库操作方法需要指定 statement 的id,这里存在硬编码,不得于开发维护
- 调用 sqlSession 的数据库操作方法时传入的变量,由于 sqlsession 方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
使用 Mybatis 的 mapper 接口开发 Dao
思路:
编写 *mapper.xml 映射文件,设计 mapper 接口(相当于 Dao 接口),遵循 MyBatis mapper 接口开发规范,框架自动生成 mapper 接口类的代理对象,自动调用 *mapper.xml 中对应的 sql
mapper 接口开发规范:
- 在 *mapper.xml 中定义 namespace 等于 mapper 接口地址
- 在 mapper 接口(Dao 接口)中的方法名要与 *mapper.xml 中 statement 的 id 一致。
- 在 mapper 接口(Dao 接口)中的输入参数类型要与 *mapper.xml 中 parameterType 指定的参数类型一致。
- 在 mapper 接口(Dao 接口)中的返回值类型要与 *mapper.xml 中 resultType 指定的类型一致。
实现:
- 设计 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;
}
- 编写 *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
- 功能测试
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();
}
}
}
- 思考
mapper 解决的问题:
遵循 mapper 接口开发规范,就会由 MyBatis 对 SqlSession 接口方法的调用进行统一生成
sqlSession.selectOne()
sqlSession.selectList()
sqlSession.insert()
sqlSession.delete()
sqlSession.update()
···
- 注意事项
mapper 接口方法参数只能有一个
MyBatis sql 执行过程分析
面向用户的关键对象分析
- SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 用于创建 SqlSessionFacoty,SqlSessionFacoty 一旦创建完成就不需要SqlSessionFactoryBuilder 了,因为 SqlSession 是通过 SqlSessionFactory 生产,所以可以将SqlSessionFactoryBuilder 当成一个工具类使用,最佳使用范围是方法范围即方法体内局部变量。 - SqlSessionFactory
SqlSessionFactory 是一个接口,接口中定义了 openSession 的不同重载方法,SqlSessionFactory 的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理 SqlSessionFactory。 - SqlSession
SqlSession 中封装了对数据库的操作,如:查询、插入、更新、删除等。
SqlSession 是通过 SqlSessionFactory 创建 ,而 SqlSessionFactory 是通过 SqlSessionFactoryBuilder 进行创建。
SqlSession 是一个面向用户的接口, sqlSession 中定义了数据库操作,默认使用 DefaultSqlSession 实现类。
SQL 执行过程
- 加载配置信息(mybatis-config.xml、*mapper.xml)
mybatis-config.xml 全局配置
- 数据源信息
···
- mapper 接口 与 sql 的映射文件地址
···
*mapper.xml 中为 mapper 接口对应 sql 定义,参数、返回数据与 java 对象的映射关系
通过查看 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?)
- 创建数据库链接
- 创建事务对象
- 创建 Executor (执行器)
- 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