EchoDemo's Blogs

mybatis学习笔记(4)-开发dao方法

疏雨洗天清,枕簟凉生。井桐一叶做秋声。谁念客身轻似叶,千里飘零?

梦断古台城,月淡潮平。便须携酒访新亭。不见当时王谢宅,烟草青青。

—-宋·邓剡《浪淘沙·疏雨洗天清》

SqlSession使用范围

(1)SqlSessionFactoryBuilder

通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。

(2)SqlSessionFactory

通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。将来mybatis和spring整合后,使用单例模式管理sqlSessionFactory。

(3)SqlSession

SqlSession是一个面向用户(程序员)的接口。SqlSession中提供了很多操作数据库的方法。如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。

SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性(比如org.apache.ibatis.session.Configuration和java.sql.Connection)。SqlSession最佳应用场合在方法体内,定义成局部变量使用。



"SqlSession"


原始dao开发方法

dao接口

package com.echodemo.mybatislearning.dao;

import com.echodemo.mybatislearning.bean.User;

import java.util.List;

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接口实现类

package com.echodemo.mybatislearning.dao.Impl;

import com.echodemo.mybatislearning.bean.User;
import com.echodemo.mybatislearning.dao.UserDao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

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 {
        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();
    }
}

测试代码

package com.echodemo.mybatislearning;

import com.echodemo.mybatislearning.bean.User;
import com.echodemo.mybatislearning.dao.Impl.UserDaoImpl;
import com.echodemo.mybatislearning.dao.UserDao;
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;

public class UserDaoImplTest {
    private SqlSessionFactory sqlSessionFactory;

    // 此方法是在执行testFindUserById之前执行
    @Before
    public void setUp() throws Exception {
        // 创建sqlSessionFactory,mybatis配置文件
        String resource = "SqlMapConfig.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(3);
        System.out.println(user.toString());
    }
}

总结原始dao开发问题

1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。

2、调用sqlsession方法时将statement的id硬编码了

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

mapper代理方法

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

2、程序员还需要编写mapper.xml映射文件

3、程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口
实现类代理对象。

开发规范

(1)在UserMapper.xml中namespace等于mapper接口地址

<!--
 namespace 命名空间,作用就是对sql进行分类化管理,理解为sql隔离
 注意:使用mapper代理方法开发,namespace有特殊重要的作用,namespace等于mapper接口地址
 -->
<mapper namespace="com.echodemo.mybatislearning.mapper.UserMapper">

1、UserMapper.java接口中的方法名和UserMapper.xml中statement的id一致

2、UserMapper.java接口中的方法输入参数类型和UserMapper.xml中statement的parameterType指定的类型一致。

3、UserMapper.java接口中的方法返回值类型和UserMapper.xml中statement的resultType指定的类型一致。

<select id="findUserById" parameterType="int" resultType="com.iot.mybatis.po.User">
    SELECT * FROM  user  WHERE id=#{value}
</select>

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

//总结:以上开发规范主要是对类似下边代码进行统一生成:
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.insert("test.insertUser", user);

代码

(1)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">
<!--
 namespace 命名空间,作用就是对sql进行分类化管理,理解为sql隔离
 注意:使用mapper代理方法开发,namespace有特殊重要的作用
 -->
<mapper namespace="com.echodemo.mybatislearning.mapper.UserMapper">
    <!-- 在映射文件中配置很多sql语句 -->
    <!--需求:通过id查询用户表的记录 -->
    <!-- 通过select执行数据库查询
     id:标识映射文件中的sql,称为statement的id
     将sql语句封装到mappedStatement对象中,所以将id称为statement的id
     parameterType:指定输入参数的类型
     #{}标示一个占位符,
     #{value}其中value表示接收输入参数的名称,如果输入参数是简单类型,那么#{}中的值可以任意。

     resultType:指定sql输出结果的映射的java对象类型,select指定resultType表示将单条记录映射成java对象
     -->
    <select id="findUserById" parameterType="int" resultType="user">
        SELECT * FROM  user  WHERE id=#{value}
    </select>

    <!-- 根据用户名称模糊查询用户信息,可能返回多条
    resultType:指定就是单条记录所映射的java对象类型
    ${}:表示拼接sql串,将接收到参数的内容不加任何修饰拼接在sql中。
    使用${}拼接sql,引起 sql注入
    ${value}:接收输入参数的内容,如果传入类型是简单类型,${}中只能使用value
     -->
    <select id="findUserByName" parameterType="string" resultType="user">
        SELECT * FROM user WHERE username LIKE '%${value}%'
    </select>

    <insert id="insertUser" parameterType="User">
        <!--
         将插入数据的主键返回,返回到user对象中

         SELECT LAST_INSERT_ID():得到刚insert进去记录的主键值,只适用于自增主键
         keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性
         order:SELECT LAST_INSERT_ID()执行顺序,相对于insert语句来说它的执行顺序
         resultType:指定SELECT LAST_INSERT_ID()的结果类型
          -->
        <selectKey keyProperty="id" order="AFTER" resultType="integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user (id,username,birthday,sex,address)values (#{id},#{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>

    <!-- 删除 用户
        根据id删除用户,需要输入 id值
         -->
    <delete id="deleteUser" parameterType="integer">
        delete from user where id=#{id}
    </delete>

    <!-- 根据id更新用户
    分析:
    需要传入用户的id
    需要传入用户的更新信息
    parameterType指定user对象,包括 id和更新信息,注意:id必须存在
    #{id}:从输入 user对象中获取id属性值
     -->
    <update id="updateUser" parameterType="user">
        update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
        where id=#{id}
    </update>
</mapper>

(2)在SqlMapConfig.xml中加载映射文件

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

    <!-- 批量加载mapper
    指定mapper接口的包名,mybatis自动扫描包下边所有mapper接口进行加载
    遵循一些规范:需要将mapper接口类名和mapper.xml映射文件名称保持一致,
    且在一个目录中上边规范的前提是:使用的是mapper代理方法
     -->
    <package name="com.echodemo.mybatislearning.mapper"/>
</mappers>

有两种方式,每种方式都要在pom文件中的build标签里面加入相应的路径指引:

<resources>
    <!-- mapper中通过resource方法一次加载一个映射文件的路径依赖指引 -->
    <!--<resource>-->
        <!--<directory>src/main/java/com/echodemo/mybatislearning/mapper</directory>-->
        <!--<includes>-->
            <!--<include>**/*.xml</include>-->
        <!--</includes>-->
    <!--</resource>-->

    <!-- 批量加载mapper指定mapper接口的包名的路径依赖指引 -->
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.xml</include>
        </includes>
    </resource>
</resources>

(3)UserMapper.java

package com.echodemo.mybatislearning.mapper;

import com.echodemo.mybatislearning.bean.User;

import java.util.List;

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;
}

(4)UserMapperTest.java

package com.echodemo.mybatislearning;

import com.echodemo.mybatislearning.bean.User;
import com.echodemo.mybatislearning.mapper.UserMapper;
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;

public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    //注解Before是在执行本类所有测试方法之前先调用这个方法
    @Before
    public void setup() throws Exception{
        //创建SqlSessionFactory
        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代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //调用userMapper的方法
        User user = userMapper.findUserById(3);
        System.out.println(user.getUsername());
    }
}

总结

(1)代理对象内部调用selectOne或selectList

1、如果mapper方法返回单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库。

2、如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。

(2)mapper接口方法参数只能有一个是否影响系统开发

mapper接口方法参数只能有一个,系统是否不利于扩展维护?系统框架中,dao层的代码是被业务层公用的。即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。

注意:持久层方法的参数可以包装类型、map…等,service方法中建议不要使用包装类型(不利于业务层的可扩展)。

改文章转载自:mybatis学习笔记(4)-开发dao方法

🐶 您的支持将鼓励我继续创作 🐶
-------------本文结束感谢您的阅读-------------