MyBatis快速入门第三讲——dao层开发方法

在实际开发中,我们总归是要开发dao层的,使用MyBatis开发dao层,通常有两个方式,即原始dao开发方式和Mapper接口开发方式。在本文中我会使用MyBatis这个框架开发dao层来一一实现以下功能:

  1. 根据用户id查询用户信息;
  2. 根据用户名称模糊查询用户信息列表;
  3. 添加用户信息。

温馨提示:本文案例代码的编写是建立在前文《MyBatis快速入门第二讲——MyBatis的快速入门》案例基础之上的!

MyBatis常用API的使用范围

在使用MyBatis这个框架开发dao层之前,你应该知道一下MyBatis常用的API。

SqlSessionFactoryBuilder的使用范围

SqlSessionFactoryBuilder用于创建SqlSessionFactory,SqlSessionFactory一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产的,所以可以将SqlSessionFactoryBuilder当成一个工具类使用,最佳使用范围是方法范围(即方法体内局部变量)。

SqlSessionFactory的使用范围

SqlSessionFactory是一个接口,接口中定义了openSession的不同重载方法,SqlSessionFactory的最佳使用范围是整个应用运行期间,一旦创建后可以重复使用,通常以单例模式管理SqlSessionFactory。

SqlSession的使用范围

SqlSession中封装了对数据库的操作,如查询、插入、更新、删除等。通过SqlSessionFactory创建SqlSession,而SqlSessionFactory是通过SqlSessionFactoryBuilder进行创建的。
SqlSession是一个面向程序员的接口,SqlSession中定义了一些数据库操作方法,所以SqlSession作用是操作数据库,并且SqlSession对象要存储数据库连接、事务和一级缓存结构等。
每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能共享使用,它是线程不安全的(多线程访问系统,当多线程同时使用一个SqlSession对象时会造成数据冲突问题)。由于SqlSession对象是线程不安全的,因此它的最佳使用范围是请求或方法范围(也可说为SqlSession的最佳使用场合是在方法体内作为局部变量来使用),绝对不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
打开一个SqlSession,记得使用完毕就要关闭它。通常会把这个关闭操作放到finally代码块中以确保每次都能执行该关闭操作。

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

原始dao开发方式

原始dao开发方式需要程序员自己编写dao层接口以及其实现类。首先,我们要在工程的src目录下创建一个com.meimeixia.mybatis.dao包,并在该包下编写一个UserDao接口。

package com.meimeixia.mybatis.dao;

import java.util.List;

import com.meimeixia.mybatis.pojo.User;

/**
 * 用户信息持久化接口
 * @author liayun
 *
 */
public interface UserDao {

	/**
	 * 根据用户ID查询用户信息
	 * @param id
	 * @return
	 */
	User getUserById(Integer id);
	
	/**
	 * 根据用户名查找用户列表
	 * @param userName
	 * @return
	 */
	List<User> getUserByUserName(String userName);
	
	/**
	 * 添加用户
	 * @param user
	 */
	void insertUser(User user);

}

然后,再在src目录下创建一个com.meimeixia.mybatis.dao.impl包,在该包下编写以上UserDao接口的一个实现类(即UserDaoImpl.java)。

package com.meimeixia.mybatis.dao.impl;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.meimeixia.mybatis.dao.UserDao;
import com.meimeixia.mybatis.pojo.User;
import com.meimeixia.mybatis.utils.SqlSessionFactoryUtils;

/**
 * 用户信息持久化实现
 * @author liayun
 *
 */
public class UserDaoImpl implements UserDao {

	@Override
	public User getUserById(Integer id) {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		User user = sqlSession.selectOne("user.getUserById", id);
		sqlSession.close();
		return user;
	}

	@Override
	public List<User> getUserByUserName(String userName) {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		List<User> list = sqlSession.selectList("user.getUserByUserName", userName);
		sqlSession.close();
		return list;
	}

	@Override
	public void insertUser(User user) {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		sqlSession.insert("user.insertUser", user);
		sqlSession.commit();
		sqlSession.close();
	}
	
}

其中,有一点需要说明,由于SqlSessionFactory是一个接口,而且接口中定义了openSession的不同重载方法,如下图所示。
MyBatis快速入门第三讲——dao层开发方法_第1张图片
所以,UserDaoImpl实现类里面的insertUser方法也可以写成下面这样。

@Override
public void insertUser(User user) {
	SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession(true);
	sqlSession.insert("user.insertUser", user);
	sqlSession.close();
}

接着,创建一个JUnit的单元测试类,即UserDaoTest.java,对UserDao接口进行单元测试。详细步骤如下:

  1. 在工程下新建一个源码目录,例如test,专门用于存放JUnit的单元测试类。
  2. 右键UserDao.java文件,在弹出的下拉列表中选中New,再在弹出的下拉列表中选中JUnit Test Case。
    MyBatis快速入门第三讲——dao层开发方法_第2张图片
  3. 在弹出的对话框中,修改单元测试类的存放目录为test源码目录以及存放的包名为com.meimeixia.mybatis.test。
    MyBatis快速入门第三讲——dao层开发方法_第3张图片
  4. 点击Next按钮,在弹出的对话框中勾选全部测试方法,最后点击Finish完成。
    MyBatis快速入门第三讲——dao层开发方法_第4张图片
    这时,便会产生如下图所示的效果。
    MyBatis快速入门第三讲——dao层开发方法_第5张图片

紧接着,将以上JUnit的单元测试类(即UserDaoTest.java)的内容修改成下面这个样子。

package com.meimeixia.mybatis.test;

import static org.junit.Assert.*;

import java.util.Date;
import java.util.List;

import org.junit.Test;

import com.meimeixia.mybatis.dao.UserDao;
import com.meimeixia.mybatis.dao.impl.UserDaoImpl;
import com.meimeixia.mybatis.pojo.User;

public class UserDaoTest {

	@Test
	public void testGetUserById() {
		UserDao userDao = new UserDaoImpl();
		User user = userDao.getUserById(31);
		System.out.println(user);
	}

	@Test
	public void testGetUserByUserName() {
		UserDao userDao = new UserDaoImpl();
		List<User> list = userDao.getUserByUserName("范");
		for (User user : list) {
			System.out.println(user);
		}
	}

	@Test
	public void testInsertUser() {
		UserDao userDao = new UserDaoImpl();
		User user = new User();
		user.setUsername("开原——范德彪");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("开原市");
		userDao.insertUser(user);
	}

}

读者可对以上方法一一进行测试,我想是不会有任何问题的,我就不再这里进行测试了(偷懒了)。

原始dao开发方式所带来的问题

从以上UserDaoImpl实现类的代码,可以看出原始dao开发方式存在以下问题:

  • dao层接口实现类的方法中存在着大量的重复代码,这些重复的代码就是模板代码。这些模板代码为:

    1. 先创建SqlSession
    2. 再调用SqlSession的方法
    3. 再提交SqlSession
    4. 再关闭SqlSession

    设想能否将这些代码提取出来,因为这可以大大减轻程序员的工作量。

  • 调用SqlSession的数据库操作方法需要指定Statement的id,这里存在硬编码,不便于开发维护。

  • 调用SqlSession方法时传入的变量,由于SqlSession方法使用泛型,即使变量类型传入错误,在编译阶段也不会报错,这不利于程序员开发。

于是,下面我将使用mapper代理方法来开发dao层,来解决上面我们所发现的问题。

Mapper动态代理开发方式

开发规范

Mapper动态代理开发方式只需要程序员编写Mapper接口(相当于dao层的接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体与上边dao层接口的实现类方法体相似。注意:Mapper动态代理开发需要遵循以下规范:

  1. Mapper.xml文件中的namespace与Mapper接口的类路径相同,即namespace必须是接口的全限定名;
  2. Mapper接口的方法名和Mapper.xml文件中定义的每个Statement的id相同;
  3. Mapper接口方法的输入参数类型和Mapper.xml文件中定义的每个SQL的parameterType的类型相同;
  4. Mapper接口方法的输出参数类型和Mapper.xml文件中定义的每个SQL的resultType的类型相同。

只要遵循了以上四个开发规则,那么我们就可以使用Mapper动态代理开发方式来开发dao层了。

编写Mapper.xml(映射文件)

之前,我们在config源码目录下新建了一个mybatis的普通文件夹,该文件夹专门用于存放映射文件。这里,我们可以在该文件夹下创建一个名为UserMapper.xml的映射文件,其内容如下:




<mapper namespace="com.meimeixia.mybatis.mapper.UserMapper">
	
	<select id="getUserById" parameterType="int" resultType="com.meimeixia.mybatis.pojo.User">
		SELECT
			`id`,
			`username`,
			`birthday`,
			`sex`,
			`address` 
		FROM
			`user` 
		WHERE 
			id = #{id2}
	select>
	
	
	<select id="getUserByUserName" parameterType="string" resultType="com.meimeixia.mybatis.pojo.User">
		SELECT
			`id`,
			`username`,
			`birthday`,
			`sex`,
			`address` 
		FROM
			`user` 
		WHERE
			`username` LIKE '%${value}%'
	select>
	
	
	<insert id="insertUser" parameterType="com.meimeixia.mybatis.pojo.User" useGeneratedKeys="true" keyProperty="id">
		INSERT INTO `user` ( `username`, `birthday`, `sex`, `address` ) VALUE ( #{username}, #{birthday}, #{sex}, #{address} )
	insert>
mapper>

编写Mapper接口

在工程的src目录下新建一个com.meimeixia.mybatis.mapper包,并在该包下创建一个Mapper接口,即UserMapper.java。

package com.meimeixia.mybatis.mapper;

import java.util.List;

import com.meimeixia.mybatis.pojo.User;

/**
 * 用户信息持久化接口
 * @author liayun
 *
 */
public interface UserMapper {

	/**
	 * 根据用户ID查询用户信息
	 * @param id
	 * @return
	 */
	User getUserById(Integer id);
	
	/**
	 * 根据用户名查找用户列表
	 * @param userName
	 * @return
	 */
	List<User> getUserByUserName(String userName);
	
	/**
	 * 添加用户
	 * @param user
	 */
	void insertUser(User user);

}

以上接口的定义有如下特点:

  1. Mapper接口的方法名和Mapper.xml文件中定义的Statement的id相同;
  2. Mapper接口方法的输入参数类型和Mapper.xml文件中定义的Statement的parameterType的类型相同;
  3. Mapper接口方法的输出参数类型和Mapper.xml文件中定义的Statement的resultType的类型相同。

加载Mapper.xml映射文件

要想加载Mapper.xml映射文件,得在SqlMapConfig.xml文件添加如下配置。
MyBatis快速入门第三讲——dao层开发方法_第6张图片
如此一来,SqlMapConfig.xml文件的整个内容就变成下面这个样子了。



<configuration>
	
	<environments default="development">
		<environment id="development">
			
			<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="liayun" />
			dataSource>
		environment>
		<environment id="test">
			
			<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="liayun" />
			dataSource>
		environment>
	environments>
	
	
	<mappers>
		
		<mapper resource="mybatis/user.xml" />
		<mapper resource="mybatis/UserMapper.xml" />
	mappers> 
configuration>

编写测试程序

编写UserMapper接口的一个单元测试类(即UserMapperTest.java),具体步骤我已在本文中详细说明了。最终,我们要将UserMapperTest这个单元测试类的内容修改成下面这个样子。

package com.meimeixia.mybatis.test;

import static org.junit.Assert.*;

import java.util.Date;
import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import com.meimeixia.mybatis.mapper.UserMapper;
import com.meimeixia.mybatis.pojo.User;
import com.meimeixia.mybatis.utils.SqlSessionFactoryUtils;

public class UserMapperTest {

	@Test
	public void testGetUserById() {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		//获取接口的代理实现类,只不过不需要我们去写了
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		User user = userMapper.getUserById(33);
		System.out.println(user);
		sqlSession.close();
	}

	@Test
	public void testGetUserByUserName() {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		//获取接口的代理实现类,只不过不需要我们去写了
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		List<User> list = userMapper.getUserByUserName("范");
		for (User user : list) {
			System.out.println(user);
		}
		sqlSession.close();
	}

	@Test
	public void testInsertUser() {
		SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
		//获取接口的代理实现类,只不过不需要我们去写了
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		User user = new User();
		user.setUsername("赵子龙");
		user.setSex("1");
		user.setBirthday(new Date());
		user.setAddress("广州黄埔刘村");
		userMapper.insertUser(user);
		sqlSession.commit();
		sqlSession.close();
	}

}

读者可对以上方法一一进行测试,我想是不会有任何问题的,我就不再这里进行测试了(偷懒了)。

小结

selectOne和selectList

动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据Mapper接口方法的返回值来决定的,如果返回List集合则调用selectList方法,如果返回单个对象则调用selectOne方法。

namespace

MyBatis官方推荐使用Mapper动态代理开发方式来开发dao层,程序员就不用编写Mapper接口实现类了。使用这种开发方式时,输入参数可以使用pojo包装对象或map对象,以保证dao的通用性。

你可能感兴趣的:(MyBatis快速入门第三讲——dao层开发方法)