继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

MyBatis3.2.x从入门到精通之第三章

HansonQ
关注TA
已关注
手记 21
粉丝 334
获赞 3452

第一节 前言:先说一点没有用的东西,不想看直接跳过前面这段。其实吧,我所认为的程序员都是比较懒的,因为大部分时间都是比较清闲的,忙碌的时候大概就是新需求的开发,项目上线等等。而开发时间其实也很短,时间应该花在熟悉业务需求,这部分时间大概花了70%,剩下的就是开发时间了。为什么会花费那么多时间在熟悉业务需求,因为这个就得涉及到设计专家的事情了,大部分设计专家都是懂的业务的,但是他们不一定懂程序,客户说什么就是什么,最后坑了程序员。
第二节 技术描述
言归正传:本章节的知识点就是Mybatis是如何开发持久层的。为了对比出mybatis的开发优势,我将分为两个部分展开叙述包括:1、传统开发持久层方式 2、mybatis代理开发持久层方式。
第三节 程序引导
开发之前,先看几个类:
SqlSessionFactoryBuilder:
1、通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory,将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理SqlSessionFactoryBuilder。
2、在需要创建SqlSessionFactory时候,只需要new一次SqlSessionFactoryBuilder即可。
SqlSessionFactory:
1、通过SqlSessionFactory创建SqlSession,使用单例模式管理sqlSessionFactory(工厂一旦创建,使用一个实例)。
2、当mybatis和spring整合后,使用单例模式管理sqlSessionFactory。
SqlSession:
1、SqlSession是一个面向用户(程序员)的接口。
2、SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象)。
3、SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。解决方案:SqlSession最佳应用场合在方法体内,定义成局部变量使用。
一、传统开发持久层方式:
思路:程序员需要开发持久层接口(面向接口编程)和持久层实现类,以及需要在实现类中注入上面提到的SqlSessionFactory这个工厂类。
来一起看程序代码:
Dao接口内容如下:

package cn.mybatis.dao;

import cn.mybatis.pojo.User;
/**
 * 
 * @ClassName: UserDao   
 * @Description: TODO(持久层接口)   
 * @author: Hanson
 * @date: 2016年1月3日 下午1:26:11   
 *
 */
public interface UserDao {
    // 根据ID查询用户信息
    public User findUser(int id) throws Exception;

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

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

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

Dao接口实现类如下:

package cn.mybatis.dao.impl;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import cn.mybatis.dao.UserDao;
import cn.mybatis.pojo.User;

/**
 * 
 * @ClassName: UserDaoImpl   
 * @Description: TODO(Dao实现类)   
 * @author: Hanson
 * @date: 2016年1月3日 下午1:28:51   
 *
 */
public class UserDaoImpl implements UserDao {
    private SqlSessionFactory sqlSessionFactory;

    // 构造器注入
    public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

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

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

    @Override
    public void updateUser(User user) throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        sqlSession.update("test.updateUser", user);
        sqlSession.commit();
        sqlSession.close();
    }

}

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">
<mapper namespace="test">
    <select id="findUser" parameterType="int" resultType="cn.mybatis.pojo.User">
        SELECT
        *
        FROM USER WHERE id=#{id}
    </select>

    <insert id="insertUser" parameterType="cn.mybatis.pojo.User">
        INSERT INTO
        USER(username,sex,address,birthday)VALUES(#{username},#{sex},#{address},#{birthday})
    </insert>

    <delete id="deleteUser" parameterType="int">
        delete from user where
        id=#{value}
    </delete>

    <update id="updateUser" parameterType="cn.mybatis.pojo.User">
        update user set
        username=#{username},address=#{address}
        where id=#{id}
    </update>
</mapper>

mybatis全局配置文件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" />
            <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="root" />
            </dataSource>
        </environment>
    </environments>
    <!-- 加载映射文件 -->
    <mappers>
        <mapper resource="sqlmap/User.xml" />
    </mappers>
</configuration>

测试代码:

package cn.mybatis.test;

import java.util.Date;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import cn.mybatis.dao.UserDao;
import cn.mybatis.dao.impl.UserDaoImpl;
import cn.mybatis.pojo.User;

public class UserDaoImplTest {

    @Before
    public void setUp() throws Exception {
        /*
         * SqlSessionFactory sqlFactory = new
         * SqlSessionFactoryBuilder().build(Resources
         * .getResourceAsStream("SqlMapConfig.xml"));
         */
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testInsertUser() throws Exception{
        /**
         * 构造器
         */
        UserDao userDao = new UserDaoImpl(//
                new SqlSessionFactoryBuilder()//
                .build(Resources
                        .getResourceAsStream("SqlMapConfig.xml")));
        User user  = new User ();
        //user.setId(26);主键自增长
        user.setSex("1");
        user.setUsername("cc");
        user.setBirthday(new Date());
        user.setAddress("NanJing");
        userDao.insertUser(user);
    }
    @Test
    public void testDeleteUser() throws Exception{
        UserDao userDao = new UserDaoImpl(//
                new SqlSessionFactoryBuilder()//
                .build(Resources
                        .getResourceAsStream("SqlMapConfig.xml")));
        userDao.deleteUser(35);
    }
    @Test
    public void testUpdateUser() throws Exception{
        UserDao userDao = new UserDaoImpl(//
                new SqlSessionFactoryBuilder()//
                .build(Resources
                        .getResourceAsStream("SqlMapConfig.xml")));
        User user = new User();
        user.setId(35);
        user.setUsername("CC");
        user.setAddress("南京");
        userDao.updateUser(user);
    }
    @Test
    public void testFindUser() throws Exception {
        UserDao userDao = new UserDaoImpl(//
                new SqlSessionFactoryBuilder()//
                .build(Resources
                        .getResourceAsStream("SqlMapConfig.xml")));
        User user  = userDao.findUser(1);
        System.out.println(user);
    }

}

原始开发Dao方式总结:
1、dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
2、调用sqlsession方法时将statement的id硬编码了
3、调用sqlsession方法时传入的变量,由于sqlsession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。
二、来看Mybatis代理方式开发持久层:
思路:使用代理开发得遵循代理开发的规范。
1、程序员还需要编写mapper.xml映射文件
2、程序员编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
代理开发规范很重要先来看下规范其实之前两张里面应该说过,这里就贴一下规范要求,还能记住的同学可以直接跳过了这个规范了

<!-- 
    编写mapper接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
    规范:
      1、namespace等于mapper接口地址
      2、mapper.java接口中的方法名等于mapper.xml中statement的id一致
      3、mapper.java接口中的方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
      4、mapper.java接口中的方法的返回值类型和mapper.xml中statement的resultType指定的类型一致。
 -->

图片描述
图片描述

以上规范主要是对以下代码进行统一:

User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.insert("test.insertUser", user);

UserMapper接口等同于传统方式开发的Dao接口:

package cn.mybatis.mapper;

import java.util.List;

import cn.mybatis.pojo.User;

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

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

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

    // 根据用户名查询用户信息

    public List<User> findUserByName(String username) throws Exception;
}

UserMapp.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接口需要遵循一些开发规范,mybatis可以自动生成mapper接口实现类代理对象。
    规范:
      1、namespace等于mapper接口地址
      2、mapper.java接口中的方法名等于mapper.xml中statement的id一致
      3、mapper.java接口中的方法的输入参数类型和mapper.xml中statement的parameterType指定的类型一致
      4、mapper.java接口中的方法的返回值类型和mapper.xml中statement的resultType指定的类型一致。
 -->
<mapper namespace="cn.mybatis.mapper.UserMapper">
    <!-- id查找 -->
    <select id="findUserById" parameterType="int" resultType="cn.mybatis.pojo.User">
        select * from user where id =#{value}
    </select>
    <!-- 添加 -->
    <insert  id="insertUser" parameterType="cn.mybatis.pojo.User">
        INSERT INTO USER(username,sex,address,birthday,id)VALUES(#{username},#{sex},#{address},#{birthday},#{id})
    </insert>
    <!-- 删除 -->
    <delete id="deleteUser" parameterType="int">
        delete from user where id=#{value}
    </delete>
    <!-- 更新 -->
    <update id="updateUser" parameterType="cn.mybatis.pojo.User">
        update user set
        username=#{username},birthday=#{birthday},sex=#{sex},address=#{address}
        where id=#{id}
    </update>
    <!-- name查找 -->
    <select id="findUserByName" parameterType="string" resultType="cn.mybatis.pojo.User">
        SELECT * FROM USER WHERE USERNAME LIKE '%${value}%'
    </select>

</mapper>

几个重要的配置文件:
db.properties:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql

log4j.properties:

log4j.rootLogger=DEBUG, Console

#Console  
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.Console.layout=org.apache.log4j.PatternLayout  
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n  

log4j.logger.java.sql.ResultSet=INFO  
log4j.logger.org.apache=INFO  
log4j.logger.java.sql.Connection=DEBUG  
log4j.logger.java.sql.Statement=DEBUG  
log4j.logger.java.sql.PreparedStatement=DEBUG

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"></properties>
    <!-- 和spring整合后 environments配置将废除 -->
    <environments default="development">
        <environment id="development">
            <!-- 使用jdbc管理事务有mybatis -->
            <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>
    <!-- 加载映射文件 -->
    <mappers>
        <!-- 加载单个映射文件方式 
        <mapper resource="mapper/UserMapper.xml" />-->
        <!-- mapper代理。。加载映射文件方式class要求mapper.xml和mapper.java的名字相同且在同一个目录下
        <mapper class="cn.mybatis.mapper.UserMapper"/> -->
        <!-- 批量加载mapper.xml文件方式。。推荐使用 -->
        <package name="cn.mybatis.mapper"/>
    </mappers>
</configuration>

测试代码:

package cn.mybatis.mapper;

import java.util.Date;
import java.util.List;

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.After;
import org.junit.Before;
import org.junit.Test;

import cn.mybatis.pojo.User;

public class UserMapperTest {
    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void setUp() throws Exception {
        sqlSessionFactory = new SqlSessionFactoryBuilder()//
        .build(Resources//
                .getResourceAsStream("SqlMapConfig.xml"));
    }

    @After
    public void tearDown() throws Exception {

    }

    @Test
    public void testFindUserById() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //生成代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = userMapper.findUserById(1);
        System.out.println(user);
    }
    @Test
    public void testFindUserByName() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = userMapper.findUserByName("小明");
        for(User user : list){
            System.out.println(user);
        }
    }
    @Test
    public void testInsertUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(100);
        user.setAddress("无锡");
        user.setSex("男");
        user.setUsername("王小虎");
        user.setBirthday(new Date());
        userMapper.insertUser(user);
        sqlSession.commit();
    }
    @Test
    public void testDeleteUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        userMapper.deleteUser(100);
    }
}

写到这里就已经结束:
能够对比出代理方式和传统方式的区别,代理方式能够让程序元只专注于sql的编写。这样能够大大减轻开发者的工作。但是Mapper代理开发也是存在问题的,下面我也列出一些,欢迎大家补充:

1、代理对象内部调用selectOne或selectList?
如果mapper方法返回单个pojo对象(非集合对象),代理对象内部通过selectOne查询数据库。
如果mapper方法返回集合对象,代理对象内部通过selectList查询数据库。
2、mapper接口参数只能有一个是否影响系统开发?
mapper接口方法参数只能有一个,系统是否不利于扩展维护。
系统框架中,dao层的代码是被业务层公用的。
即使mapper接口只有一个参数,可以使用包装类型的pojo满足不同的业务方法的需求。
注意:持久层方法的参数可以包装类型、map。。。,service方法中建议不要使用包装类型(不利于业务层的可扩展)。
3、补充一个${}拼接会引起sql注入,个人的一点点小建议就是把常用的方法写成一个工具类,譬如校验是否为空,是否不是空,返回数据响应格式等等,这样方便将来开发不然每次都得重写。程序员嘛,重复的事情做一遍就好了。
解决方案如下代码:就是把需要模糊查询的字段用%拼接然后再使用#{}
package com.hanson.utils;
/**
 * 
 * @ClassName: StringUtils   
 * @Description: TODO(字符串工具类)   
 * @author: Hanson
 * @date: 2016年1月3日 下午2:08:24   
 *
 */
public class StringUtils {
    /**
     * 
     * @Title: isEmpty
     * @Description: TODO (判断是否是空)
     * @Author: Hanson                
     * @Create Date: 2016年1月3日 下午2:09:07
     * @History: 2016年1月3日 下午2:09:07 Hanson Created.
     *
     * @param str
     * @return
     *
     */
    public static boolean isEmpty(String str){
        if(str==null||"".equals(str.trim())){
            return true;
        }else{
            return false;
        }
    }
    /**
     * 
     * @Title: isNotEmpty
     * @Description: TODO (判断是否不是空)
     * @Author: Hanson                
     * @Create Date: 2016年1月3日 下午2:09:17
     * @History: 2016年1月3日 下午2:09:17 Hanson Created.
     *
     * @param str
     * @return
     *
     */
    public static boolean isNotEmpty(String str){
        if((str!=null)&&!"".equals(str.trim())){
            return true;
        }else{
            return false;
        }
    }
    /**
     * 
     * @Title: formatLike
     * @Description: TODO (格式化模糊查询)
     * @Author: Hanson                
     * @Create Date: 2016年1月3日 下午2:09:27
     * @History: 2016年1月3日 下午2:09:27 Hanson Created.
     *
     * @param str
     * @return
     *
     */
    public static String formatLike(String str){
        if(isNotEmpty(str)){
            return "%"+str+"%";
        }else{
            return null;
        }
    }
}

有什么疑问可以贴在下面。
下一章:输入映射,有时间会接着写的。


本文为慕课网作者原创,转载请标明【原文作者及本文链接地址】。侵权必究,谢谢合作!


打开App,阅读手记
27人推荐
发表评论
随时随地看视频慕课网APP

热门评论

大神写的很好,可以把源码包放出来吗?

我先说下我的思路,我不知道你具体是怎么实现的。 首先根据findUserById能查询出一个User的话。 //查询出id为1的用户 User user = userMapper.findUserById(1); //接着更新这个用户的话 user.setName("新名字"); ... //需要注意的是 按照mapper.xml中是根据id去更新用户的,也就是要修改这个查询出来的用户。除了id这个字段不可以修改其他都是允许的。不然是不一定能够找到你给定id的用户

您好,我按照你的代码,在mapper中添加一个更新的方法, 如下: public void updateUser(User user) throws Exception; junit中调用时首先查询findUserById方法,查询出user,然后更新,但是发现查询方法可以查到,但是更新方法没有能够实现,请教大神告诉我原因,我试着重新创建sqlsession,但是也不好使。

查看全部评论