(转)MyBatis框架的学习(三)——Dao层开发方法
http://blog.csdn.net/yerenyuan_pku/article/details/71700957
使用MyBatis开发Dao层,通常有两个方法,即原始Dao开发方法和Mapper接口开发方法。本文案例代码的编写是建立在前文MyBatis框架的学习(二)——MyBatis架构与入门案例基础之上的!
需求
明确开发需求,在实际开发中,我们总归是要开发Dao层的,所以在本文中我使用MyBatis这个框架技术开发Dao层来将以下功能一一实现:
- 根据用户id查询一个用户信息
- 根据用户名称模糊查询用户信息列表
- 添加用户信息
MyBatis常用API的使用范围
在讲解使用MyBatis这个框架技术开发Dao层之前,首先来说一下MyBatis常用的API。
SqlSessionFactoryBuilder的使用范围
SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要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接口和Dao实现类。
在工程的src目录下创建一个cn.itheima.mybatis.dao包,并在该包下编写一个UserDao接口:
public interface UserDao {
User getUserById(int id);
List<User> getUserByName(String username);
void insertUser(User user);
}
接着再在src目录下创建一个cn.itheima.mybatis.dao.impl包,在该包下编写UserDao接口的实现类——UserDaoImpl.java:
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User getUserById(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
// 根据id查询用户信息
User user = sqlSession.selectOne("getUserById", id);
// 关闭sqlSession
sqlSession.close();
return user;
}
@Override
public List<User> getUserByName(String username) {
// 创建一个SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 执行查询
List<User> list = sqlSession.selectList("getUserByName", username);
// 释放资源
sqlSession.close();
return list;
}
@Override
public void insertUser(User user) {
// 创建一个SQLSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 插入用户
sqlSession.insert("insertUser", user);
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
}
}
最后创建一个JUnit的测试类——UserDaoTest.java,对UserDao接口进行测试。步骤如下:
- 在工程下新建一个源码目录,专门用于存放JUnit的单元测试类。
- 右键【UserDao.java】→【New】→【JUnit Test Case】
- 在弹出的对话框中,修改单元测试类的存放目录为test源码目录
- 点击【Next】按钮,在弹出的对话框中勾选全部测试方法,然后点击【Finish】完成
这样就会产生如下效果:
最后将JUnit的单元测试类——UserDaoTest.java修改为:
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory = null; // 工厂对象一般在我们的系统中是单例的
@Before
public void init() throws IOException {
// 第一步,创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,加载配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,创建SqlSessionFactory对象
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testGetUserById() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.getUserById(10);
System.out.println(user);
}
@Test
public void testGetUserByName() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
List<User> list = userDao.getUserByName("张");
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testInsertUser() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = new User();
user.setUsername("赵云");
user.setAddress("正定");
user.setBirthday(new Date());
user.setSex("1");
userDao.insertUser(user);
}
}
- 1
- 2
读者可自行测试,在此不做过多赘述。
原始Dao开发方式所带来的问题
从以上UserDaoImpl类的代码可看出原始Dao开发存在以下问题:
dao接口实现类方法中存在大量的重复代码,这些重复的代码就是模板代码。
模板代码为:- 先创建sqlsession
- 再调用sqlsession的方法
- 再提交sqlsession
- 再关闭sqlsession
设想能否将这些代码提取出来,这可大大减轻程序员的工作量。
- 调用sqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不得于开发维护。
- 调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
下面我来使用mapper代理方法来开发Dao层,来解决上面我们所发现的问题。
Mapper动态代理开发方式
开发规范
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
- Mapper.xml文件中的namespace与mapper接口的类路径相同,即namespace必须是接口的全限定名。
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同。
- Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
接下来我就来编码使用mapper动态代理方式来开发Dao层。
编写Mapper.xml(映射文件)
我们可在config源码目录下新建一个mapper的普通文件夹,该文件夹专门用于存放映射文件。然后在该文件夹下创建一个名为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">
<mapper namespace="cn.itheima.mybatis.mapper.UserMapper">
<select id="getUserById" parameterType="int" resultType="USer">
select * from user where id=#{id};
</select>
<select id="getUserByName" parameterType="string" resultType="cn.itheima.mybatis.po.User">
SELECT * FROM `user` WHERE username LIKE '%${value}%'
</select>
<insert id="insertUser" parameterType="cn.itheima.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `user` (username,birthday,sex,address) VALUES (#{username},#{birthday},#{sex},#{address})
</insert>
</mapper>
- 1
编写Mapper接口
在工程的src目录下新建一个cn.itheima.mybatis.mapper包,并在该包下创建一个Mapper接口——UserMapper.java:
public interface UserMapper {
User getUserById(int id);
List<User> getUserByName(String username);
void insertUser(User user);
}
接口定义有如下特点:
- mapper接口方法名和mapper.xml中定义的statement的id相同。
- mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同。
- mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同。
加载mapper.xml映射文件
在SqlMapConfig.xml文件添加如下配置:
<mapper resource="mapper/mapper.xml"/>
如此一来,SqlMapConfig.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事务管理 -->
<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="yezi" />
</dataSource>
</environment>
</environments>
<!-- 加载mapper文件 -->
<mappers>
<!-- resource是基于classpath来查找的 -->
<mapper resource="sqlmap/user.xml"/>
<mapper resource="mapper/mapper.xml"/>
</mappers>
</configuration>
编写测试程序
最后编写UserMapper接口的一个单元测试类,具体步骤我已在本文中详细说明了。修改UserMapperTest这个单元测试类的内容为:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory = null; // 工厂对象一般在我们的系统中是单例的
@Before
public void init() throws IOException {
// 第一步,创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,加载配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,创建SqlSessionFactory对象
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testGetUserById() {
// 和Spring整合后就省略了
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获得代理对象(到时候就只需要通过Spring容器拿到UserMapper接口的代理对象就可以了)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(10);
System.out.println(user);
// 和Spring整合后就省略了
sqlSession.close();
}
@Test
public void testGetUserByName() {
// 和Spring整合后就省略了
SqlSession sqlSession = sqlSessionFactory.openSession();
// 获得代理对象(到时候就只需要通过Spring容器拿到UserMapper接口的代理对象就可以了)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.getUserByName("张");
for (User user : list) {
System.out.println(user);
}
// 和Spring整合后就省略了
sqlSession.close();
}
@Test
public void testInsertUser() {
// 读者自己编写代码测试......
}
}
- 1
以上方法测试均没问题,读者如若不信可亲测。
小结
selectOne和selectList
动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。
namespace
mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用pojo包装对象或map对象,保证dao的通用性。
SqlMapConfig.xml配置文件
配置内容
SqlMapConfig.xml文件中配置的内容和顺序如下:
- properties(属性)
- settings(全局配置参数)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境集合属性对象)
- environment(环境子属性对象)
- transactionManager(事务管理)
- dataSource(数据源)
- environment(环境子属性对象)
- mappers(映射器)
properties(属性)
在SqlMapConfig.xml配置文件中,我们可把数据库连接信息配置到properties标签当中,类似如下:
<!-- 配置属性 -->
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
</properties>
接下来把以上配置信息添加到SqlMapConfig.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>
<!-- 配置属性 -->
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
</properties>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="root" />
<property name="password" value="yezi" />
</dataSource>
</environment>
</environments>
<!-- 加载mapper文件 -->
<mappers>
<!-- resource是基于classpath来查找的 -->
<mapper resource="sqlmap/user.xml"/>
<mapper resource="mapper/mapper.xml"/>
</mappers>
</configuration>
- 1
SqlMapConfig.xml文件向上面这样配置,虽然没问题,但是我们不觉得这样很不爽吗?我们一般都是将数据库连接信息配置到一个java属性文件中,然后再来引用其中的配置信息。我按照这种指导思想在classpath下定义一个db.properties文件,SqlMapConfig.xml文件引用该属性文件中的配置信息如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=yezi
如此一来,SqlMapConfig.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>
<!-- 配置属性 -->
<properties resource="db.properties">
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
</properties>
<!-- 和spring整合后 environments配置将废除 -->
<environments default="development">
<environment id="development">
<!-- 使用jdbc事务管理 -->
<transactionManager type="JDBC" />
<!-- 数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 加载mapper文件 -->
<mappers>
<!-- resource是基于classpath来查找的 -->
<mapper resource="sqlmap/user.xml"/>
<mapper resource="mapper/mapper.xml"/>
</mappers>
</configuration>
注意:MyBatis将按照下面的顺序来加载属性:
- 在properties元素体内定义的属性首先被读取
- 然后会读取properties元素中resource或url加载的属性,它会覆盖已读取的同名属性
按照我自己的话说就是:先加载property元素内部的属性,然后再加载db.properties文件外部的属性,如果有同名属性则会覆盖。
如若读者不信,可以将properties元素的内容修改为:
<properties resource="db.properties">
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="jdbc.username" value="hello"/>
</properties>
以上故意将数据库连接的用户名给整错,结果发现仍然好使,这足以说明问题了。
typeAliases(类型别名)
mybatis支持别名
mybatis支持别名如下表:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
自定义别名
如果我们在SqlMapConfig.xml配置文件添加如下配置信息:
<!-- 配置pojo的别名 -->
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias type="cn.itheima.mybatis.po.User" alias="user"/>
</typeAliases>
- 1
- 2
以上就为User类定义了一个别名(user)。
那么我们就可以在mapper.xml映射文件中使用这个别名了,如下:
<mapper namespace="cn.itheima.mybatis.mapper.UserMapper">
<!-- 别名不区分大小写 -->
<select id="getUserById" parameterType="int" resultType="USer">
select * from user where id=#{id};
</select>
......
</mapper>
- 1
注意:resultType属性的值就是User类的别名,且别名是不区分大小写的。
聪明的小伙伴们肯定发现如果像这样为每一个pojo定义一个别名,并不是明智之举,万一一个项目里面有很多pojo类呢?所以我们可以批量定义别名,如下:
<!-- 配置pojo的别名 -->
<typeAliases>
<!-- 批量别名定义,扫描包的形式创建别名,别名就是类名,且不区分大小写 -->
<package name="cn.itheima.mybatis.po"/>
</typeAliases>
- 1
SqlMapConfig.xml文件加载mapper.xml文件
Mapper(映射器)配置的几种方法:
<mapper resource=" " />
使用相对于类路径的资源,如<mapper resource="sqlmap/user.xml"/>
- 1
- 1
<mapper class=" " />
使用mapper接口类路径,如:<mapper class="cn.itheima.mybatis.mapper.UserMapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。<package name=""/>
注册指定包下的所有mapper接口,如:<package name="cn.itheima.mybatis.mapper"/>
注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。
虽然Mapper(映射器)配置有以上三种方法,但是实际开发中就用第三种方法。至此,使用MyBatis开发Dao层我就总结到这里。
(转)MyBatis框架的学习(三)——Dao层开发方法的更多相关文章
- (转)MyBatis框架的学习(四)——Mapper.xml文件中的输入和输出映射以及动态sql
http://blog.csdn.net/yerenyuan_pku/article/details/71893689 前面对MyBatis框架的学习中,我们对Mapper.xml映射文件多少有些了解 ...
- SSM框架之Mybatis(3)dao层开发
Mybatis(3)dao层开发 以实现类完成CRUD操作 1.持久层dao层接口的书写 src\main\java\dao\IUserDao.java package dao; import dom ...
- (转)MyBatis框架的学习(六)——MyBatis整合Spring
http://blog.csdn.net/yerenyuan_pku/article/details/71904315 本文将手把手教你如何使用MyBatis整合Spring,这儿,我本人使用的MyB ...
- (转)MyBatis框架的学习(二)——MyBatis架构与入门
http://blog.csdn.net/yerenyuan_pku/article/details/71699515 MyBatis框架的架构 MyBatis框架的架构如下图: 下面作简要概述: S ...
- mybatis实战教程(mybatis in action)之十:mybatis SqlSessionSupport 的使用,构件DAO 层的应用
前面的系列mybatis 文章,已经基本讲到了mybatis的操作,但都是基于mapper隐射操作的,在mybatis 3中这个mapper 接口貌似充当了以前在ibatis 2中的 DAO 层的作用 ...
- 基于Mybatis的Dao层开发
转自:https://www.cnblogs.com/rodge-run/p/6528398.html 基于Mybatis的Dao层开发 SqlSessionFactoryBuilder用于创建 Sq ...
- (转)MyBatis框架的学习(七)——MyBatis逆向工程自动生成代码
http://blog.csdn.net/yerenyuan_pku/article/details/71909325 什么是逆向工程 MyBatis的一个主要的特点就是需要程序员自己编写sql,那么 ...
- (转)MyBatis框架的学习(五)——一对一关联映射和一对多关联映射
http://blog.csdn.net/yerenyuan_pku/article/details/71894172 在实际开发中我们不可能只是对单表进行操作,必然要操作多表,本文就来讲解多表操作中 ...
- JAVAEE——Mybatis第一天:入门、jdbc存在的问题、架构介绍、入门程序、Dao的开发方法、接口的动态代理方式、SqlMapConfig.xml文件说明
1. 学习计划 第一天: 1.Mybatis的介绍 2.Mybatis的入门 a) 使用jdbc操作数据库存在的问题 b) Mybatis的架构 c) Mybatis的入门程序 3.Dao的开发方法 ...
随机推荐
- B - Alyona and Mex
Description Someone gave Alyona an array containing n positive integers a1, a2, ..., an. In one oper ...
- E20190212-mt
创建: 2019/02/12 reserve n. 储备; 保留; 保护区; 替补队员; vt. 储备; 保留; 预约; vi. 预订; slot n. 位置; 狭槽,水沟; [人名] ...
- 使用you-get下载网页小视频(实际上你可以下载任意你想要的web网页中的内容)
1. 什么是you-get? You-Get是一个小型的命令行实用程序,用于从Web下载媒体内容(视频,音频,图像),如果没有其他方便的方法可以尝试使用you-get. 2.安装you-get 打开命 ...
- 小程序地区时间自定义选择器 picker
进入微信公众平台小程序开发文档搜索 picker 点进去后下滑,点击在开发者工具中预览即可
- bzoj 2738: 矩阵乘法【整体二分+树状数组】
脑子一抽开始写主席树,敲了一会发现不对-- 整体二分,用二维树状数组维护值为当前区间的格子个数,然后根据k的大小和当前询问的子矩阵里的值和k的大小关系来决定这个询问放在哪一部分向下递归 #includ ...
- 【OpenJ_Bailian - 2795】金银岛(贪心)
金银岛 Descriptions: 某天KID利用飞行器飞到了一个金银岛上,上面有许多珍贵的金属,KID虽然更喜欢各种宝石的艺术品,可是也不拒绝这样珍贵的金属.但是他只带着一个口袋,口袋至多只能装重量 ...
- nmon性能监控工具学习
nmon在AIX环境上,是一款很出名的系统性能监控工具,其实它除了可以运行在AIX,还可以在Linux环境下编译.使用. 源码下载地址: http://nmon.sourceforge.net/pmw ...
- JAVA团队开发手册 - 3. 开发流程
开发流程 对于一个项目,最大的问题就是如何拆解为任务,分配到合适的人手里,并在有限的时间内完成它. 就像做建筑工程一样,其实做IT也是可以量化的,可能有的人砌砖砌得慢一些,有的人快一些. 但是我们把整 ...
- jdbc学习day1
- D. Merge Equals(from Educational Codeforces Round 42 (Rated for Div. 2))
模拟题,运用强大的stl. #include <iostream> #include <map> #include <algorithm> #include < ...