学习Mybatis框架(二)—开发Dao层

mybatis开发dao两种方法

  • 原始dao开发方法(程序需要编写dao接口和dao实现类)
  • mybaits的mapper接口(相当于dao接口)代理开发方法

从 XML 中构建 SqlSessionFactory

不使用 XML 构建 SqlSessionFactory这里不讨论了
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。

//导入mybatis配置文件
String resource = "SqlMapConfig.xml";
//得到配置文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建会话工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 配置文件(configuration XML)中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。XML 配置文件的详细内容前面探讨了。

从 SqlSessionFactory 中获取 SqlSession

既然有了 SqlSessionFactory ,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

SqlSession sqlSession= sqlSessionFactory.openSession();
try {
  //sqlSession.selectOne结果是与映射文件中所匹配的resultType类型的参数
  User user=sqlSession.selectOne("test.findUserById", 1);
} finally {
  sqlSession.close();
}

诚然这种方式能够正常工作,并且对于使用旧版本 MyBatis 的用户来说也比较熟悉,不过现在有了一种更直白的方式。使用对于给定语句能够合理描述参数和返回值的接口(比如说BlogMapper.class),你现在不但可以执行更清晰和类型安全的代码,而且还不用担心易错的字符串字面值以及强制类型转换。
例如:

SqlSession sqlSession= sqlSessionFactory.openSession();
try {
        //创建UserMapper接口的实例对象,是mybatis自动生成Usermapper代理对象(很重要!!!)
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);

        //调用userMapper 方法 
        User user=userMapper.findUserById(29);
} finally {
  sqlSession.close();
}

作用域(Scope)和生命周期

对象生命周期和依赖注入框架
依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器(mapper)并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。

SqlSessionFactoryBuilder
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。
通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory,将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。

SqlSessionFactory
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式
通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory。

SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:

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

一、原始dao开发方法

(需要写dao接口和dao实现类)

需要向dao实现类中注入SqlSessionFactory,在方法体内通过SqlSessionFactory创建SqlSession
这里写图片描述

第一步:编写配置sql语句的映射文件.xml文件—User.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进行分类化管理 -->
<mapper namespace="test">

<!--1、根据用户id(主键)查询用户信息
id:标识映射文件中的sql,将sql语句封装到preparedStatement对象中,所以将id成为statement的id
#{}:表示占位符
#{id}:id表示输入参数,参数名称就是id,如果输入参数是简单类型,则#{}中参数名可以任意,可以value或者其他名称
resultType:指定sql输出结果所映射的java对象类型,select指定resultType表示单条记录映射的java对象
-->
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">
SELECT*FROM USER WHERE id=#{id}
</select>


<!--2、 根据用户名称模糊查询用户信息 -->
<!--${value}:拼接符号,不建议使用,接收输入参数的内容,如果传入类型是简单类型,${}中只能使用value-->
<select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.pojo.User">
<!-- SELECT * FROM USER WHERE USERNAME LIKE'%张三%' -->
SELECT * FROM USER WHERE USERNAME LIKE '%${value}%'
</select>


<!-- 3、添加用户 -->
<!-- 自增主键返回 
keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
order:相对于insert来说,它的执行顺序
resultType:指定的结果类型 -->

<insert id="insertUser" parameterType="cn.itcast.mybatis.pojo.User">

<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>

insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
<!-- 
使用mysql的uuid()生成主键
执行过程:
首先通过uuid()得到主键,将主键设置到user对象的id属性中
其次在insert执行时,从user对象中取出id属性值
 -->
<!--  
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
    SELECT uuid()
</selectKey>
insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})
 -->   
</insert>

<!-- 4、删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>

<!-- 5、更新用户 -->
<update id="updateUser" parameterType="cn.itcast.mybatis.pojo.User">
update user set
username = #{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>

</mapper>

第二步:编写 dao接口——UserDao

public interface UserDao {
    //根据id查询用户信息
    public User findUserById(int id) throws Exception;

    //根据用户名列查询用户列表
    public List<User> findUserByName(String name) throws Exception;

    //添加用户信息
    public void insertUser(User user) throws Exception;

    //删除用户信息
    public void deleteUser(int id) throws Exception;
}

第三步: 编写dao接口实现类——UserDaoImpl

public class UserDaoImpl implements UserDao {

    // 需要向dao实现类中注入SqlSessionFactory
    // 这里通过构造方法注入
    private SqlSessionFactory sqlSessionFactory;

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public User (int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        sqlSession.close();
        return user;
    }

    @Override
    public List<User> findUserByName(String name) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> list = sqlSession.selectList("test.findUserByName", name);
        sqlSession.close();
        return list;
    }

    @Override
    public void insertUser(User user) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.insert("test.insertUser", user);//执行插入操作
        sqlSession.commit();// 提交事务
        sqlSession.close();// 释放资源
    }

    @Override
    public void deleteUser(int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.delete("test.deleteUser", id);//执行删除操作
        sqlSession.commit();// 提交事务
        sqlSession.close();
    }
}

第四步:在SqlMapConfig.xml文件中加载.xml映射文件

<!-- 1、通过resource方法一次加载一个映射文件 -->
        <mapper resource="sqlmap/User.xml" /> 

第五步: 编写测试代码

编写UserDao接口实现类的UserDaoImpl一个单元测试类,点击UserDaoImpl类——new——Junit Test Case——sourcefolder选为test目录下——勾选setUp()——点击next(选需要测试函数)——点击Finish
这里写图片描述

public class UserDaoImplTest {
    private SqlSessionFactory sqlSessionFactory;

    // 此方法是在执行testFindUserById之前执行,为了创建sqlSessionFactory
    @Before
    public void setUp() throws Exception {
        // mybatis配置文件
        String resource = "SqlMapConfig.xml";
        // 得到配置文件流
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 创建会话工厂,传入mybatis的配置文件信息 
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

//这里只写了一个测试代码testFindUserById
    @Test
    public void testFindUserById() throws Exception {
        // 创建UserDao的对象
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);

        // 调用UserDao的方法
        User user = userDao.findUserById(1);

        System.out.println(user);
    }
}

测试结果运行图如下:
这里写图片描述

二、原始dao方法开发存在的问题

在dao接口实现类UserDaoImpl 中存在问题:

  1. 存在大量代码冗余,一些模板方法可以提取出来
//创建sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 提交事务
sqlSession.commit();
// 释放资源
sqlSession.close();
  1. 调用sqlSession数据库操作方法,需要指定statement的Id,存在硬编码
//statement=namespace.id
//<T> T selectOne(String statement, Object parameter)
User user=sqlSession.selectOne("test.findUserById", 1);
  1. sqlSession方法使用泛型,即使传参类型错误,编译阶段也不报错,不利于开发。
@Override
    public User findUserById(int id) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = sqlSession.selectOne("test.findUserById", id);
        sqlSession.close();
        return user;
    }

三、 Mapper动态代理开发方法

(程序员只需要mapper接口(相当 于dao接口))

mapper代理开发规范

程序员编写mapper接口时,如果遵循mapper开发规范,mybatis可以自动生成mapper接口实现类代理对象

mapper开发规范:
举例 UserMapper.xml ———————— UserMapper.java
* 1、mapper.xml中namespace=mapper接口的全限定名
这里写图片描述
* 2、mapper.java接口中方法名=mapper.xml中statement的id
* 3、mapper.java接口中的方法输入参数类型和mapper.xml中statement的parameterType指定的类型一致。
* 4、mapper.java接口中方法返回值类型和mapper.xml中statement中resultType指定的类型一致*/
这里写图片描述

第一步:编写配置sql语句的映射文件Mapper.xml

在config源码目录下新建一个mapper的普通文件夹,该文件夹专门用于存放映射文件。然后在该文件夹下创建映射文件Mapper.xml—如UserMapper.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">

<!-- 在映射文件中配置sql语句 -->
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">

<!--1、根据用户id(主键)查询用户信息
id:标识映射文件中的sql,将sql语句封装到preparedStatement对象中,所以将id成为statement的id
#{id}:#{}:表示占位符,id表示输入参数,参数名称就是id,如果输入参数是简单类型,则#{}中参数名可以任意,可以value或者其他名称
resultType:指定sql输出结果所映射的java对象类型,select指定resultType表示单条记录映射的java对象
-->
<select id="findUserById" parameterType="int" resultType="cn.itcast.mybatis.pojo.User">
SELECT*FROM USER WHERE id=#{id}
</select>


<!--2、 根据用户名称模糊查询用户信息 -->
<!--${value}:拼接符号,不建议使用,接收输入参数的内容,如果传入类型是简单类型,${}中只能使用value
-->
<select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.pojo.User">
<!-- SELECT * FROM USER WHERE USERNAME LIKE'%张三%' -->
SELECT * FROM USER WHERE USERNAME LIKE '%${value}%'
</select>


<!-- 3、添加用户 -->
<!-- 自增主键返回 
keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
order:相对于insert来说,它的执行顺序
resultType:指定的结果类型 -->

<insert id="insertUser" parameterType="cn.itcast.mybatis.pojo.User">

<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>

insert into user(username,birthday,sex,address) value(#{username},#{birthday},#{sex},#{address})
<!-- 
使用mysql的uuid()生成主键
执行过程:
首先通过uuid()得到主键,将主键设置到user对象的id属性中
其次在insert执行时,从user对象中取出id属性值
 -->
<!--  
<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.String">
    SELECT uuid()
</selectKey>
insert into user(id,username,birthday,sex,address) value(#{id},#{username},#{birthday},#{sex},#{address})
 -->   
</insert>

<!-- 4、删除用户 -->
<delete id="deleteUser" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>

<!-- 5、更新用户 -->
<update id="updateUser" parameterType="cn.itcast.mybatis.pojo.User">
update user set
username = #{username},birthday=#{birthday},sex=#{sex},address=#{address}
where id=#{id}
</update>

</mapper>

第二步:编写Mapper接口

在src目录下新建一个cn.itheima.mybatis.mapper包,并在该包下创建一个Mapper接口——UserMapper.java:
这里写图片描述

package cn.itcast.mybatis.mapper;
import java.util.List;
import cn.itcast.mybatis.pojo.User;

public interface UserMapper {
        //根据id查询用户信息
        public User findUserById(int id) throws Exception;

        //根据用户名列查询用户列表
        public List<User> findUserByName(String name) throws Exception;

        //添加用户信息
        public void insertUser(User user) throws Exception;

        //删除用户信息
        public void deleteUser(int id) throws Exception;

        //更新用户
        public void updateUser(User user)throws Exception;
}

第三步:在SqlMapConfig.xml文件中加载Mapper.xml

在SqlMapConfig.xml文件mappers标签对里面添加如下配置:

<mapper resource="mapper/UserMapper.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事务管理,事务控制由mybatis管理-->
            <transactionManager type="JDBC" />
        <!-- 数据库连接池,由mybatis管理-->
            <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="123" />
            </dataSource>
        </environment>
    </environments>


        <!-- 加载映射文件 -->
    <mappers>
        <!-- 1、通过resource加载单个映射文件 -->
        <mapper resource="sqlmap/User.xml" /> 

        <!-- 2、通过mapper接口加载单个映射文件————class="mapper接口全限定名"
                 遵循规范:a)使用的是mapper代理方法 
                          b)需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中 -->
        <!--
         <mapper class="cn.itcast.mybatis.mapper.UserMapper" />  
         -->

         <!--3、批量加载mapper映射文件————name="mapper接口所在的包名",mybatis自动扫描包下边所有mapper接口进行加载
               遵循规范:a)使用的是mapper代理方法 
                        b)需要将mapper接口类名和mapper.xml映射文件名称保持一致,且在一个目录中-->
        <package name="cn.itcast.mybatis.mapper"/>
    </mappers> 

</configuration>

加载映射文件三种方式:(推荐第3种方式)
这里写图片描述

第四步:编写测试程序

编写UserMapper接口的一个单元测试类,选中UserMapper.java接口——new——Junit Test Case——sourcefolder选为test目录下——勾选setUp()——点击Finish
这里写图片描述

public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception {
        // mybatis配置文件
        String resource = "SqlMapConfig.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 userMapper=sqlSession.getMapper(UserMapper.class);

        //调用userMapper 方法 
        User user=userMapper.findUserById(29);

        System.out.println(user);   
    }

    @Test
    public void testFindUserByName() throws Exception {

        SqlSession sqlSession = sqlSessionFactory.openSession();

        //创建UserMapper对象,mybatis自动生成mapper代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //调用userMapper的方法
        List<User> list = userMapper.findUserByName("小明");

        sqlSession.close();

        System.out.println(list);       
    }
}

猜你喜欢

转载自blog.csdn.net/hefenglian/article/details/80616487