1.背景
在上篇中我们使用了MyBatis实现简单的增删改查,并没有进行dao层的封装,所以在这里我们进行dao层开发:
(1)传统的jdbc dao层开发(写dao接口,实现dao接口类)
(2)mapper代理方法 (只需要写mapper接口类,相当于dao接口类)
在Mybatis操作的时候,我们使用的是SqlSession 来进行数据库操作,其中SqlSession的使用范围:
通过SqlSessionFactoryBuilder创建SqlSessionFactory ,而SqlSession通过SqlSessionFactory 创建,所以使用单例模式管理SqlSessionFactory,将来使用mybatis和spring整合后,使用单例模式管理SqlSessionFactory;
SqlSession的作用是:
1)提供接口,很多操作数据库的方法,如:selectOne ,selectList
2)线程不安全,在sqlSession实现类中除了有接口中的方法,还有数据域的属性;
3)最佳应用的场合在方法体内,定义成局部变量使用。
2.原始dao开发实现
(1)dao接口
package mybatie.dao;
import mybatis.po.FClient;
/**
* 用户接口
*
* 作者:原明卓
* 时间:2015年12月21日 上午10:00:00
* 描述:TODO
*/
public interface ClientDao {
FClient findClientById(int id) throws Exception;
void updateClient(FClient f) throws Exception;
void deleteClient(int id) throws Exception;
void insertClient(FClient f) throws Exception;
}
(2)实现dao接口
实现数据库操作的几个方法:
package mybatie.dao;
import mybatis.po.FClient;
import oracle.net.aso.s;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
public class ClientDaoImp implements ClientDao {
private SqlSessionFactory sqlSessionFactory;
public ClientDaoImp(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public FClient findClientById(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
FClient c = sqlSession.selectOne("test.findClientById", id);
sqlSession.close();
return c;
}
@Override
public void updateClient(FClient f) {
SqlSession sqlSession=sqlSessionFactory.openSession();
sqlSession.update("test.updateClient",f);
sqlSession.commit();
sqlSession.close();
}
@Override
public void deleteClient(int id) {
SqlSession session = sqlSessionFactory.openSession();
session.delete("test.deleteClient", id);
session.commit();
session.close();
}
@Override
public void insertClient(FClient f) {
SqlSession session = sqlSessionFactory.openSession();
session.insert("test.insertClient", f);
session.commit();
session.close();
}
}
(3)测试类
public class TestClient {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws Exception {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
@Test
public void test() {
fail("Not yet implemented");
}
@Test
public void findClientById() throws Exception {
ClientDao clientDao = new ClientDaoImp(sqlSessionFactory);
FClient c = clientDao.findClientById(1);
System.out.println(c);
}
}
(4)原始的dao层遇到的问题
1)dao接口实现类方法中存在大量的模板方法,设想能否将这些代码提取出来
2)调用sqlsession方法的时候将statement存在硬编码
3)调用sqlsession的方法的时候,传入参数为Object 类型,及时传入错误,也不会报错
3.使用Mapper代理的方式实现
基本步骤为 :
1)编写mapper.xml映射文件
2)编写Mapper接口,相当于dao接口
3)mybatis可以自动生成mapper接口的实现类代理对象
(1)实现Mapper映射文件
mapper.xml 规范 :
1)namespace 等于mapper接口地址
2)mapper.java 接口中的方法和mapper.xml中的statement 的id一致
3)mapper.java 接口中的方法的参数和mapper.xml中的statement 的paramterType类型一致
4)mapper.java 接口中的方法的返回值和mapper.xml中的statement的resultType类型一致
总结 :mapper.xml实现的规范,就是对SqlSession接口中的方法进行统一的生成
比如 :
<?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代理的方法开发,namespace有特殊的重要作用,namespace为mapper接口的地址 -->
<mapper namespace="mybatie.mapper.ClientMapper">
<!-- 在映射文件中配置很多的sql语句 -->
<!-- 通过select 执行数据库查询 id:表示映射文件的sql, 将sql语句封装到mappedStatement对象中,所以将id称为statement的id
#{}: 表示一个占位符,相当于jdbc中的? parameterType : 指定参数类型,比如指定为int #{id} : 其中的id表示接入输入的参数,参数名称就是id,如果输入的参数是简单类型
#{}中参数名可以任意,可以value或其他名称; resultType :指定sql输出的结果的映射java对象类型,select指定的resultType表示将单条记录映射成java对象 -->
<!-- 根据id查用户 -->
<select id="findClientById" parameterType="int" resultType="mybatis.po.FClient">
select * from f_client where id=#{id}
</select>
<!-- 根据用户名模糊查询 resultType :指定的单条记录所映射的java对象类型 #{} 表示占位符 ${}:表示拼接sql串,将接收到的参数内容不加任何修饰拼接在sql中,使用${}拼接,引起sql注入
${value} :接入输入参数的内容,如果传入类型是简单类型,${}简单的 -->
<select id="findClientByName" parameterType="java.lang.String"
resultType="mybatis.po.FClient">
select *from f_client where username like '%${value}%'
</select>
<!-- 添加用户 这里注意 主键返回实现 -->
<select id="insertClient" parameterType="mybatis.po.FClient"
resultType="java.lang.Integer">
insert into
f_client(id,username,client_certificate_no,born_date,family_register_address,now_address,contact_mode,urgency_contact_mode,create_date)
values (#{id},
#{username},#{client_certificate_no},#{born_date},#{family_register_address},#{now_address},#{contact_mode},#{urgency_contact_mode},#{create_data})
</select>
<!-- 删除用户 -->
<delete id="deleteClient" parameterType="int">
delete from f_client where id=#{id}
</delete>
<!-- 更新用户 -->
<update id="updateClient" parameterType="mybatis.po.FClient">
update f_client set
username=#{username},client_certificate_no=#{client_certificate_no},born_date=#{born_date},family_register_address=#{family_register_address},now_address=#{now_address},contact_mode=#{contact_mode},urgency_contact_mode=#{urgency_contact_mode}
where id=#{id}
</update>
</mapper>
(2)mapper接口
注意接口定义的和Mapper.xml对比下,看看mapper.xml的 规范!!
package mybatie.mapper;
import java.util.List;
import mybatis.po.FClient;
/**
* mapper接口 ,对应Clientmapper.xml
* 作者:原明卓
* 时间:2015年12月21日 上午10:00:00
* 描述:TODO
*/
public interface ClientMapper {
public FClient findClientById(int id) throws Exception;
public void updateClient(FClient f) throws Exception;
public void deleteClient(int id) throws Exception;
public int insertClient(FClient f) throws Exception;
public List<FClient> findClientByName(String name) throws Exception;
}
(3)测试
private SqlSessionFactory sqlfactory;
@Before
public void setUp() throws Exception {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlfactory=new SqlSessionFactoryBuilder().build(is);
}
@Test
public void testFindClientById() throws Exception {
SqlSession session = sqlfactory.openSession();
ClientMapper mapper = session.getMapper(ClientMapper.class);
FClient fc = mapper.findClientById(1);
System.out.println(fc);
session.close();
}
4.一些问题
(1)代理对象内部调用selectOne或 selectList如果mapper方法返回单个pojo对象,代理对象内部通过selectOne查询数据库;
如果mapper方法返回集合对象,代理对象内部通过内部通过selectList查询数据库;
否则会报错误。
(2) mapper接口方法参数只能有一个是否影响系统开发
系统框架中,dao层的代码是否是被业务层公用的,即使mapper接口只有一个参数,可以使用包装
类型pojo满足不同业务方法需求;
注意:持久层方法的参数可以包装类型,map,service方法中建议不要使用包装类型,因为不利于
业务层的可扩展性。