Mybatis之原生和代理Dao实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011301372/article/details/86227218

使用Mybatis开发Dao,通常有两种方法:原始Dao开发方法和Mapper接口开发方法

SqlSession的使用范围

SqlSession中封装了对数据库的操作:查询、插入、更新、删除等
通过SQLSessionFactory创建SqlSession,而SqlSessionFactory是通过SQLSessionFactoryBuilder进行创建

  • SqlSessionFactoryBuilder

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

  • SqlSessionFactory

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

  • SqlSession

    SqlSession是一个面向用户的接口,SqlSession中定义了数据库操作方法。
    每个线程都应该有自己的SqlSession实例。SqlSession的实例不能共享使用,它也是线程不安全的。因此最佳的范围是请求或方法范围,不能将SqlSession实例的引用放在一个类的静态字段或实例字段中。
    打开一个SqlSession,使用完后要关闭它。通常把关闭操作放在finally块以确保每次都能执行关闭。

SqlSession session = SqlSessionFactory.openSesion();
try{
//do work
}finally{
	session.close();
}

原生Dao实现(编写Dao接口和Dao实现类)

  • 映射文件
<?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语句在代码中通过id进行调用,如果表单特别多,每张表单的SQL语句特别多,id可能重复-->
<!--namespace 相当于又加了一层,调用的时候通过namespace加上id进行调用,避免重名的可能-->
<!--namespace起名是有规范的-->
<mapper namespace="test">
    <!--具有增删改查对应的子标签-->

    <!--id : sql语句唯一标识-->
    <!--parameterType:指定传入参数类型,是javaBean中对应属性的类型-->
    <!--resultType : 返回结果集类型,如果返回结果为集合,可以调用selectList()方法,这个方法返回的结果就是一个集合,所以映射文件中应该配置成集合泛型的类型-->
    <select id="findUserById" parameterType="java.lang.Integer" resultType="cn.zst.domain.User">
<!--#{}占位符,起到占用的作用,如果传入的是基本类型(String long double int Boolean float等)那么 # {}中的变量名称可以随意写-->
        SELECT * from `user` WHERE id = #{id}
    </select>
    <select id ="findUserByName" parameterType="java.lang.String" resultType="cn.zst.domain.User">
      <!--  SELECT * from `user` WHERE username like #{name}-->
        <!--${}拼接符,字符串原样拼接,如果传入的参数是基本类型(String long double int boolean float等)那么${}中的变量名称必须是value-->
        <!--注意:拼接符有SQL注入的风险,所以慎重使用-->
         SELECT * from `user` WHERE username like '%${value}%'
    </select>

    <!--增加-->
    <!--如果业务需要返回数据库自增主键,可以使用SELECT LAST_INSERT_ID()-->
    <insert id="insertUser" parameterType="cn.zst.domain.User">
        <!--执行SELECT LAST_INSERT_ID()数据库函数,返回自增的主键-->
        <!--keyProperty:将返回的主键放入传入参数的id中保存,此处的传入参数为User ,-->
        <!--order:当前函数相对于insert语句的执行顺序,在insert前执行是before,在insert后执行是after-->
        <!--resultType:id 类型 ,也就是在keyproperties中属性的类型-->
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
<!--如果传入的是JavaBean类型,那么#{}中的变量名称必须是JavaBean中对应的属性.属性.属性.属性.属性-->
        <!--例如,User中有一个Customer的属性,customer有一个custname属性,则表示为 customer.custname-->
        INSERT into `user` (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
    </insert>



    <!--删除用户-->
    <delete id="delUserById" parameterType="java.lang.Integer">
        delete from `user` WHERE id=#{id}

    </delete>

    <!--更新,根据id来跟新,因为此处传入的是两个参数,所有通过user对象传入,因为它包含所有需要的属性-->
    <update id="updateUserById" parameterType="cn.zst.domain.User" >
        UPDATE `user` set username=#{username} WHERE id=#{id}

    </update>

</mapper>

  • Dao接口
public interface UserDao {
    public User findUserByID(Integer id);
    public List<User> findUserByUserName(String userName);
}
public class UserDaoImpl implements UserDao {
    private SqlSessionFactory sqlSessionFactory;
    //通过构造方法注入
    public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory=sqlSessionFactory;
    }

    @Override
    public User findUserByID(Integer id) {
        //SQLSession是线程不安全的,所以它的最佳使用范围是方法体内
        SqlSession openSession = sqlSessionFactory.openSession();
        User user = openSession.selectOne("test.findUserById", id);
        return user;

    }

    @Override
    public List<User> findUserByUserName(String userName) {
        SqlSession openSession = sqlSessionFactory.openSession();
        List<User> list = openSession.selectList("test.findUserByName", userName);
        return list;
    }
}

  • 测试类

public class UserTest {
    @Test
    public void testFindUserById() throws Exception{

        String resource="SqlMapConfig.xml";
        //通过流将核心配置文件读取出来
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //通过核心配置文件输入流来创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

        //通过工厂创建会话
        SqlSession openSession = factory.openSession();

        //第一个参数,所调用的SQL语句 = namespace+.+sql的id
        User user = openSession.selectOne("test.findUserById", 1);
        System.out.println(user);
        openSession.close();


    }




    @Test
    public void testFindUserByName() throws Exception{
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession openSession = factory.openSession();

        List<User> list = openSession.selectList("test.findUserByName", "王");
        System.out.println(list);
        openSession.close();

    }


    @Test
    public void testInsertUser() throws Exception{
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession openSession = factory.openSession();

        User user = new User();
        user.setUsername("王名");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("河南郑州");

        openSession.insert("test.insertUser",user);

        //提交事务
        //如果不提交事务,则数据不能插入到数据库中
        //在Hibernate中如果要提交事务,首先要开启事务,beganTransation
        //此处Mybatis会自动开启事务,但不知道要什么时候提交事务。所以需要手动提交事务
        openSession.commit();
        //自增主键的id
        System.out.println(user.getId());

    }
    @Test
    public void testDelUserById () throws Exception{
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession openSession = factory.openSession();

        openSession.delete("test.delUserById",28);
        //提交事务
        openSession.commit();

    }
    @Test
    public void testUpdateUserById() throws Exception{
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession openSession = factory.openSession();

        User user = new User();
        user.setUsername("王志");
        user.setId(24);
        openSession.update("test.updateUserById",user);

        openSession.commit();

    }


}

原生方式实现Dao存在的问题

  1. Dao 方法体中存在重复代码:通过SqlSessionFactory创建SqlSession,调用SqlSession的数据库操作方法
  2. 调用SqlSession的数据库操作方法需要指定statement的id,这里存在硬编码,不利于开发维护。

代理Dao实现

动态代理开发规范

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法同上边Dao接口实现类方法.Mapper接口开发需要遵循以下规范:

  • Mapper.xml文件中的namespace与mapper接口的类路径相同
  • Mapper接口方法名与Mapper.xml中定义的每个statement和id相同
  • Mapper接口方法的输入参数类型和Mapper.xml中定义的每个sql的parameterType的类型不同
  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。

映射文件(Mapper.xml)

定义mapper映射文件UserMapper.xml(内容同User.xml),需要修改namespace的值为UserMapper接口路径。UserMapper.xml在classpath下mappe目录下。

<?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接口代理实现编写规则:
1 映射文件中namespace要等于接口的全路径名称
2 映射文件中SQL语句id要等于接口的方法名称
3 映射文件中传入参数类型要等于接口方法的传入参数类型
4 映射文件中返回结果集类型要等于接口的返回值类型
-->
<mapper namespace="cn.zst.mapper.UserMapper">
    <select id="findUserById" parameterType="java.lang.Integer" resultType="cn.zst.domain.User">
         SELECT * from `user` WHERE id = #{id}

    </select>
    <select id ="findUserByName" parameterType="java.lang.String" resultType="cn.zst.domain.User">

        SELECT * from `user` WHERE username like '%${value}%'
    </select>
    <delete id="delUserById" parameterType="java.lang.Integer" >
        delete from `user` WHERE id=#{id}

    </delete>
    <update id="updateUserById" parameterType="java.lang.Integer" >
        UPDATE `user` set username=#{username} WHERE id=#{id}

    </update>
    <insert id="insertUser" parameterType="cn.zst.domain.User" >
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT into `user` (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})

    </insert>
 </mapper>

接口文件(Mapper.java)

接口定义有如下规范:

  • Mapper接口方法名和Mapper.xml中定义的statement的id相同
  • Mapper接口方法的输入参数类型和Mapper.xml中定义的statement的parameterType的类型相同
  • Mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同
public interface UserMapper {
    public User findUserById(Integer id);
    //动态代理情况中,如果返回结果集为list,那么Mybatis会在生成实现类的时候,会自动调用selectList()方法
    public List<User> findUserByName(String userName);
    public void insertUser(User user);
    public void delUserById(Integer id);

}

测试类


public class UserMapperTest {
    private SqlSessionFactory factory;

    //在测试方法前执行该方法,
    @Before
    public void setUp() throws Exception{
        String resource = "sqlMapConfig.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        factory = new SqlSessionFactoryBuilder().build(inputStream);

    }
    @Test
    public void testFindUserById() throws Exception{
        SqlSession openSession = factory.openSession();
        //通过getMapper方法来实例化接口
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        User user = mapper.findUserById(1);
        System.out.println(user);
    }
}

小结

  1. selectOne()和selectList()
    动态代理对象调用SqlSession.selectOne()和SqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象就调用selectOne()方法
  2. namespace
    Mybatis官方推荐使用mapper代理方法开发mapper接口,程序员不用编写mapper接口实现类,使用mapper代理方法时,输入参数可以使用POJO包装对象或map对象,保证dao的通用性。

猜你喜欢

转载自blog.csdn.net/u011301372/article/details/86227218
今日推荐