mybatis_mybatisplus - CSDN
mybatis 订阅
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)当前,最新版本是MyBatis 3.5.4 ,其发布时间是2020年2月4日。 展开全文
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)当前,最新版本是MyBatis 3.5.4 ,其发布时间是2020年2月4日。
信息
外文名
MyBatis
时    间
2010年
单    位
Github
来    源
apache
运行平台
java
原    名
iBatis
MyBatis基本信息
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 [1] 
收起全文
精华内容
参与话题
  • Mybatis教程-mybatis详细教程万字长文

    万次阅读 多人点赞 2020-09-21 16:55:13
    Mybatis教程。Mybatis详细教程。Mybatis是一款优秀的持久层框架。其封装了JDBC操作, 免去了开发人员编写JDBC 代码以及设置参数和获取结果集的重复性工作。通过编写简单的 XML 或Java注解即可映射数据库CRUD操作。本...

    【仅需一次订阅,作者所有专栏都能看】

    推荐 SpringBoot 教程 https://blog.csdn.net/hellozpc/article/details/107095951
    推荐 SpringCloud 教程https://blog.csdn.net/hellozpc/article/details/83692496

    文章目录

    欢迎关注公众号「程猿薇茑」
    **微信扫一扫**
    mybatis

    1.从JDBC谈起

    1.1.使用IDEA创建maven工程

    这里写图片描述
    这里写图片描述
    这里写图片描述

    1.2.引入mysql依赖包

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    

    这里写图片描述

    1.3.准备数据

    • 创建数据库:
      CREATE DATABASE ssmdemo;

    • 创建表:
      DROP TABLE IF EXISTS tb_user;
      CREATE TABLE tb_user (
      id char(32) NOT NULL,
      user_name varchar(32) DEFAULT NULL,
      password varchar(32) DEFAULT NULL,
      name varchar(32) DEFAULT NULL,
      age int(10) DEFAULT NULL,
      sex int(2) DEFAULT NULL,
      birthday date DEFAULT NULL,
      created datetime DEFAULT NULL,
      updated datetime DEFAULT NULL,
      PRIMARY KEY (id)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    • 插入数据:
      INSERT INTO ssmdemo.tb_user ( userName, password, name, age, sex, birthday, created, updated) VALUES ( ‘zpc’, ‘123456’, ‘鹏程’, ‘22’, ‘1’, ‘1990-09-02’, sysdate(), sysdate());
      INSERT INTO ssmdemo.tb_user ( userName, password, name, age, sex, birthday, created, updated) VALUES ( ‘hj’, ‘123456’, ‘静静’, ‘22’, ‘1’, ‘1993-09-05’, sysdate(), sysdate());

    1.4.JDBC基础代码回顾

    • JDBCTest.java
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    /**
     * @author Evan
     */
    public class JDBCTest {
        public static void main(String[] args) throws Exception {
            Connection connection = null;
            PreparedStatement prepareStatement = null;
            ResultSet rs = null;
    
            try {
                // 加载驱动
                Class.forName("com.mysql.jdbc.Driver");
                // 获取连接
                String url = "jdbc:mysql://127.0.0.1:3306/ssmdemo";
                String user = "root";
                String password = "123456";
                connection = DriverManager.getConnection(url, user, password);
                // 获取statement,preparedStatement
                String sql = "select * from tb_user where id=?";
                prepareStatement = connection.prepareStatement(sql);
                // 设置参数
                prepareStatement.setLong(1, 1l);
                // 执行查询
                rs = prepareStatement.executeQuery();
                // 处理结果集
                while (rs.next()) {
                    System.out.println(rs.getString("userName"));
                    System.out.println(rs.getString("name"));
                    System.out.println(rs.getInt("age"));
                    System.out.println(rs.getDate("birthday"));
                }
            } finally {
                // 关闭连接,释放资源
                if (rs != null) {
                    rs.close();
                }
                if (prepareStatement != null) {
                    prepareStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            }
        }
    }
    

    1.5.JDBC缺点分析

    这里写图片描述

    2.MyBatis介绍

    这里写图片描述
    官方文档 http://www.mybatis.org/mybatis-3/getting-started.html

    3.Mybaits整体架构

    这里写图片描述
    这里写图片描述

    4.快速入门(quick start)

    4.1.引入依赖(pom.xml)

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.2.8</version>
    </dependency>
    

    4.2.全局配置文件(mybatis-config.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="driver" value="com.mysql.jdbc.Driver"/>
    	<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true"/>
    	<property name="username" value="root"/>
        	<property name="password" value="123456"/>
       </properties>
    
       <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
       <environments default="test">
          <!-- id:唯一标识 -->
          <environment id="test">
             <!-- 事务管理器,JDBC类型的事务管理器 -->
             <transactionManager type="JDBC" />
             <!-- 数据源,池类型的数据源 -->
             <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
             </dataSource>
          </environment>
          <environment id="development">
             <!-- 事务管理器,JDBC类型的事务管理器 -->
             <transactionManager type="JDBC" />
             <!-- 数据源,池类型的数据源 -->
             <dataSource type="POOLED">
                <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
             </dataSource>
          </environment>
       </environments>
      </configuration>
    

    4.3.配置Map.xml(MyMapper.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:命名空间,随便写,一般保证命名空间唯一 -->
    <mapper namespace="MyMapper">
       <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
          resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
        -->
       <select id="selectUser" resultType="com.zpc.mybatis.User">
          select * from tb_user where id = #{id}
       </select>
    </mapper>
    

    4.4.修改全局配置文件(mybatis-config.xml)

    配上MyMapper.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>
       <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
       <environments default="test">
          <!-- id:唯一标识 -->
          <environment id="test">
             <!-- 事务管理器,JDBC类型的事务管理器 -->
             <transactionManager type="JDBC" />
             <!-- 数据源,池类型的数据源 -->
             <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
             </dataSource>
          </environment>
       </environments>
       <mappers>
         <mapper resource="mappers/MyMapper.xml" />
       </mappers>
    </configuration>
    

    4.5.构建sqlSessionFactory(MybatisTest.java)

    		// 指定全局配置文件
            String resource = "mybatis-config.xml";
            // 读取配置文件
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 构建sqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    

    4.6.打开sqlSession会话,并执行sql(MybatisTest.java)

    	    // 获取sqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession();
            // 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
            // 第二个参数:指定传入sql的参数:这里是用户id
            User user = sqlSession.selectOne("MyMapper.selectUser", 1);
            System.out.println(user);
    
    • 完整代码:
      MybatisTest.java
    mport com.zpc.test.pojo.User;
    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 java.io.InputStream;
    
    public class MybatisTest {
       public static void main(String[] args) throws Exception {
          // 指定全局配置文件
          String resource = "mybatis-config.xml";
          // 读取配置文件
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // 构建sqlSessionFactory
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          // 获取sqlSession
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
             // 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
             // 第二个参数:指定传入sql的参数:这里是用户id
             User user = sqlSession.selectOne("MyMapper.selectUser", 1);
             System.out.println(user);
          } finally {
             sqlSession.close();
          }
       }
    }
    
    

    User.java

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class User {
        private String id;
        private String userName;
        private String password;
        private String name;
        private Integer age;
        private Integer sex;
        private Date birthday;
        private String created;
        private String updated;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Integer getSex() {
            return sex;
        }
    
        public void setSex(Integer sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getCreated() {
            return created;
        }
    
        public void setCreated(String created) {
            this.created = created;
        }
    
        public String getUpdated() {
            return updated;
        }
    
        public void setUpdated(String updated) {
            this.updated = updated;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id='" + id + '\'' +
                    ", userName='" + userName + '\'' +
                    ", password='" + password + '\'' +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    ", sex=" + sex +
                    ", birthday='" + new SimpleDateFormat("yyyy-MM-dd").format(birthday) + '\'' +
                    ", created='" + created + '\'' +
                    ", updated='" + updated + '\'' +
                    '}';
        }
    }
    

    4.7.目录结构

    这里写图片描述

    5.分析

    5.1.引入日志依赖包(pom.xml)

    会自动引入log4j以及slf4j-api

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.5</version>
    </dependency>
    

    5.2.添加log4j.properties

    log4j.rootLogger=DEBUG,A1
    log4j.logger.org.apache=DEBUG
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
    

    再次运行程序会打印日志:

    2018-06-30 19:53:37,554 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-06-30 19:53:37,818 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2094411587.
    2018-06-30 19:53:37,818 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cd62f43]
    2018-06-30 19:53:37,863 [main] [MyMapper.selectUser]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-06-30 19:53:37,931 [main] [MyMapper.selectUser]-[DEBUG] ==> Parameters: 1(Integer)
    2018-06-30 19:53:37,953 [main] [MyMapper.selectUser]-[DEBUG] <==      Total: 1
    User{id='1', userName='zpc', password='123456', name='鹏程', age=25, sex=1, birthday='1990-09-02', created='2018-06-30 18:20:18.0', updated='2018-06-30 18:20:18.0'}
    2018-06-30 19:53:37,954 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cd62f43]
    2018-06-30 19:53:37,954 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cd62f43]
    2018-06-30 19:53:37,955 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2094411587 to pool.
    

    5.3.MyBatis使用步骤总结

    1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
    2)创建SqlSessionFactory
    3)通过SqlSessionFactory创建SqlSession对象
    4)通过SqlSession操作数据库 CRUD
    5)调用session.commit()提交事务
    6)调用session.close()关闭会话

    6.完整的CRUD操作

    6.1.创建UserDao接口

    import com.zpc.mybatis.pojo.User;
    import java.util.List;
    
    public interface UserDao {
    
        /**
         * 根据id查询用户信息
         *
         * @param id
         * @return
         */
        public User queryUserById(String id);
    
        /**
         * 查询所有用户信息
         *
         * @return
         */
        public List<User> queryUserAll();
    
        /**
         * 新增用户
         *
         * @param user
         */
        public void insertUser(User user);
    
        /**
         * 更新用户信息
         *
         * @param user
         */
        public void updateUser(User user);
    
        /**
         * 根据id删除用户信息
         *
         * @param id
         */
        public void deleteUser(String id);
    }
    

    6.2.创建UserDaoImpl

    import com.zpc.mybatis.dao.UserDao;
    import com.zpc.mybatis.pojo.User;
    import org.apache.ibatis.session.SqlSession;
    import java.util.List;
    
    public class UserDaoImpl implements UserDao {
        public SqlSession sqlSession;
    
        public UserDaoImpl(SqlSession sqlSession) {
            this.sqlSession = sqlSession;
        }
    
        @Override
        public User queryUserById(String id) {
            return this.sqlSession.selectOne("UserDao.queryUserById", id);
        }
    
        @Override
        public List<User> queryUserAll() {
            return this.sqlSession.selectList("UserDao.queryUserAll");
        }
    
        @Override
        public void insertUser(User user) {
            this.sqlSession.insert("UserDao.insertUser", user);
        }
    
        @Override
        public void updateUser(User user) {
            this.sqlSession.update("UserDao.updateUser", user);
        }
    
        @Override
        public void deleteUser(String id) {
            this.sqlSession.delete("UserDao.deleteUser", id);
        }
    
    }
    

    6.3.编写UserDao对应的UserDaoMapper.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:命名空间,随便写,一般保证命名空间唯一 -->
    <mapper namespace="UserDao">
        <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
           resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
         -->
        <!--<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">-->
        <!--select * from tb_user where id = #{id}-->
        <!--</select>-->
    
        <!--使用别名-->
        <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
          select
           tuser.id as id,
           tuser.user_name as userName,
           tuser.password as password,
           tuser.name as name,
           tuser.age as age,
           tuser.birthday as birthday,
           tuser.sex as sex,
           tuser.created as created,
           tuser.updated as updated
           from
           tb_user tuser
           where tuser.id = #{id};
       </select>
    
        <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
            select * from tb_user;
        </select>
    
        <!--插入数据-->
        <insert id="insertUser" parameterType="com.zpc.mybatis.pojo.User">
            INSERT INTO tb_user (
            user_name,
            password,
            name,
            age,
            sex,
            birthday,
            created,
            updated
            )
            VALUES
            (
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            now(),
            now()
            );
        </insert>
    
        <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
            UPDATE tb_user
            <trim prefix="set" suffixOverrides=",">
                <if test="userName!=null">user_name = #{userName},</if>
                <if test="password!=null">password = #{password},</if>
                <if test="name!=null">name = #{name},</if>
                <if test="age!=null">age = #{age},</if>
                <if test="sex!=null">sex = #{sex},</if>
                <if test="birthday!=null">birthday = #{birthday},</if>
                updated = now(),
            </trim>
            WHERE
            (id = #{id});
        </update>
    
        <delete id="deleteUser">
            delete from tb_user where id=#{id}
        </delete>
    </mapper>
    
    

    在mybatis-config.xml中添加配置:

    <mappers>
        <mapper resource="mappers/MyMapper.xml"/>
        <mapper resource="mappers/UserDaoMapper.xml"/>
    </mappers>
    

    6.4.添加UserDao的测试用例

    Pom文件中添加junit依赖

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    

    按住Alt+Enter,选择create test
    这里写图片描述

    这里写图片描述

    6.5.编写UserDao的测试用例

    import com.zpc.mybatis.dao.UserDao;
    import com.zpc.mybatis.dao.impl.UserDaoImpl;
    import com.zpc.mybatis.pojo.User;
    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;
    import java.util.Date;
    import java.util.List;
    
    public class UserDaoTest {
    
        public UserDao userDao;
        public SqlSession sqlSession;
    
        @Before
        public void setUp() throws Exception {
            // mybatis-config.xml
            String resource = "mybatis-config.xml";
            // 读取配置文件
            InputStream is = Resources.getResourceAsStream(resource);
            // 构建SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            // 获取sqlSession
            sqlSession = sqlSessionFactory.openSession();
            this.userDao = new UserDaoImpl(sqlSession);
        }
    
        @Test
        public void queryUserById() throws Exception {
            System.out.println(this.userDao.queryUserById("1"));
        }
    
        @Test
        public void queryUserAll() throws Exception {
            List<User> userList = this.userDao.queryUserAll();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void insertUser() throws Exception {
            User user = new User();
            user.setAge(16);
            user.setBirthday(new Date("1990/09/02"));
            user.setName("大鹏");
            user.setPassword("123456");
            user.setSex(1);
            user.setUserName("evan");
            this.userDao.insertUser(user);
            this.sqlSession.commit();
        }
    
        @Test
        public void updateUser() throws Exception {
            User user = new User();
            user.setBirthday(new Date());
            user.setName("静鹏");
            user.setPassword("654321");
            user.setSex(1);
            user.setUserName("evanjin");
            user.setId("1");
            this.userDao.updateUser(user);
            this.sqlSession.commit();
        }
    
        @Test
        public void deleteUser() throws Exception {
            this.userDao.deleteUser("4");
            this.sqlSession.commit();
        }
    
    }
    
    

    6.6.目录结构

    这里写图片描述

    6.7.解决数据库字段名和实体类属性名不一致的问题

    查询数据的时候,发现查不到userName的信息,
    User{id=‘2’, userName=‘null’, password=‘123456’, name=‘静静’, age=22, sex=0, birthday=‘1993-09-05’, created=‘2018-06-30 18:22:28.0’, updated=‘2018-06-30 18:22:28.0’}
    原因:数据库的字段名是user_name,POJO中的属性名字是userName
    两端不一致,造成mybatis无法填充对应的字段信息。修改方法:在sql语句中使用别名。
    解决方案1:在sql语句中使用别名:

    <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
       select
        tuser.id as id,
        tuser.user_name as userName,
        tuser.password as password,
        tuser.name as name,
        tuser.age as age,
        tuser.birthday as birthday,
        tuser.sex as sex,
        tuser.created as created,
        tuser.updated as updated
        from
        tb_user tuser
        where tuser.id = #{id};
    </select>
    

    解决方案2: 参考后面的resultMap –mapper具体的配置的时候

    解决方案3:参考驼峰匹配 — mybatis-config.xml 的时候

    7. 动态代理Mapper实现类

    7.1.思考上述CRUD中的问题

    1、接口->实现类->mapper.xml
    2、实现类中,使用mybatis的方式非常类似
    3、xml中的sql statement 硬编码到java代码中。

    思考:能否只写接口,不写实现类。只编写接口和Mapper.xml即可?

    因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。因此mybatis提供了接口的动态代理。

    7.2.使用动态代理改造CRUD

    • 修改测试用例的setUp方法
      这里写图片描述
    • 执行queryUserAll()方法

    这里写图片描述

    org.apache.ibatis.binding.BindingException: Type interface com.zpc.mybatis.dao.UserDao is not known to the MapperRegistry.
    	at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
    	at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:655)
    	at org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper(DefaultSqlSession.java:222)
    at com.zpc.mybatis.test.UserDaoTest.setUp(UserDaoTest.java:32)
    
    • 分析原因,在UserMapper.xml中配置接口的全路径
      mapper.xml namespace
      如果希望使用mybatis通过的动态代理的接口,就需要namespace中的值,和需要对应的Mapper(dao)接口的全路径一致。Mapper中Namespace的定义本身是没有限制的,只要不重复即可,但如果使用Mybatis的DAO接口动态代理,则namespace必须为DAO接口的全路径,例如:com.zpc.mybatis.dao.UserDao
    <mapper namespace="com.zpc.mybatis.dao.UserDao">
    

    7.3.完整的例子

    1、创建UserMapper接口(对应原UserDao)

    public interface UserMapper {
       
       /**
        * 登录(直接使用注解指定传入参数名称)
        * @param userName
        * @param password
        * @return
        */
       public User login(@Param("userName") String userName, @Param("password") String password);
       
       /**
        * 根据表名查询用户信息(直接使用注解指定传入参数名称)
        * @param tableName
        * @return
        */
       public List<User> queryUserByTableName(@Param("tableName") String tableName);
       
       /**
        * 根据Id查询用户信息
        * @param id
        * @return
        */
       public User queryUserById(Long id);
       
       /**
        * 查询所有用户信息
        * @return
        */
       public List<User> queryUserAll();
       
       /**
        * 新增用户信息
        * @param user
        */
       public void insertUser(User user);
       
       /**
        * 根据id更新用户信息
        * @param user
        */
       public void updateUser(User user);
       
       /**
        * 根据id删除用户信息
        * @param id
        */
       public void deleteUserById(Long id);
    }
    
    

    2、创建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">
    <!-- mapper:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
    <mapper namespace="com.zpc.mybatis.dao.UserMapper">
        <!--
           1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
           2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
         -->
        <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
            select * from ${tableName}
        </select>
    
        <select id="login" resultType="com.zpc.mybatis.pojo.User">
            select * from tb_user where user_name = #{userName} and password = #{password}
        </select>
    
        <!-- statement,内容:sql语句。
           id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
           resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
           parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
         -->
        <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
            select * from tb_user where id = #{id}
        </select>
        <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
            select * from tb_user
        </select>
        <!-- 新增的Statement
           id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
           parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
           useGeneratedKeys:开启主键回写
           keyColumn:指定数据库的主键
           keyProperty:主键对应的pojo属性名
         -->
        <insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
                parameterType="com.zpc.mybatis.pojo.User">
            INSERT INTO tb_user (
            id,
            user_name,
            password,
            name,
            age,
            sex,
            birthday,
            created,
            updated
            )
            VALUES
            (
            null,
            #{userName},
            #{password},
            #{name},
            #{age},
            #{sex},
            #{birthday},
            NOW(),
            NOW()
            );
        </insert>
        <!-- 
           更新的statement
           id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
           parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
         -->
        <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
            UPDATE tb_user
            <trim prefix="set" suffixOverrides=",">
                <if test="userName!=null">user_name = #{userName},</if>
                <if test="password!=null">password = #{password},</if>
                <if test="name!=null">name = #{name},</if>
                <if test="age!=null">age = #{age},</if>
                <if test="sex!=null">sex = #{sex},</if>
                <if test="birthday!=null">birthday = #{birthday},</if>
                updated = now(),
            </trim>
            WHERE
            (id = #{id});
        </update>
        <!-- 
           删除的statement
           id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
           parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
         -->
        <delete id="deleteUserById" parameterType="java.lang.String">
            delete from tb_user where id=#{id}
        </delete>
    </mapper>
    
    

    3、全局配置文件mybatis-config.xml引入UserMapper.xml

    <mappers>
        <mapper resource="mappers/MyMapper.xml"/>
        <mapper resource="mappers/UserDaoMapper.xml"/>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
    
    

    4、创建UserMapper测试用例

    import com.zpc.mybatis.dao.UserMapper;
    import com.zpc.mybatis.pojo.User;
    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;
    import java.util.Date;
    import java.util.List;
    
    public class UserMapperTest {
    
        public UserMapper userMapper;
    
        @Before
        public void setUp() throws Exception {
            // 指定配置文件
            String resource = "mybatis-config.xml";
            // 读取配置文件
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 构建sqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 获取sqlSession
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            // 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径
            // 2. 映射文件的statement的id必须和mapper接口的方法名保持一致
            // 3. Statement的resultType必须和mapper接口方法的返回类型一致
            // 4. statement的parameterType必须和mapper接口方法的参数类型一致(不一定)
            this.userMapper = sqlSession.getMapper(UserMapper.class);
        }
    
        @Test
        public void testQueryUserByTableName() {
            List<User> userList = this.userMapper.queryUserByTableName("tb_user");
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testLogin() {
            System.out.println(this.userMapper.login("hj", "123456"));
        }
    
        @Test
        public void testQueryUserById() {
            System.out.println(this.userMapper.queryUserById("1"));
        }
    
        @Test
        public void testQueryUserAll() {
            List<User> userList = this.userMapper.queryUserAll();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testInsertUser() {
            User user = new User();
            user.setAge(20);
            user.setBirthday(new Date());
            user.setName("大神");
            user.setPassword("123456");
            user.setSex(2);
            user.setUserName("bigGod222");
            this.userMapper.insertUser(user);
            System.out.println(user.getId());
        }
    
        @Test
        public void testUpdateUser() {
            User user = new User();
            user.setBirthday(new Date());
            user.setName("静静");
            user.setPassword("123456");
            user.setSex(0);
            user.setUserName("Jinjin");
            user.setId("1");
            this.userMapper.updateUser(user);
        }
    
        @Test
        public void testDeleteUserById() {
            this.userMapper.deleteUserById("1");
        }
    }
    
    

    目录结构:
    这里写图片描述

    7.4.动态代理总结

    这里写图片描述

    8.mybatis-config.xml详解

    mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序
    这里写图片描述

    8.1.properties属性读取外部资源

    properties配置的属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:

    <properties resource="org/mybatis/example/config.properties">
      <property name="username" value="dev_user"/>
      <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
    

    然后其中的属性就可以在整个配置文件中被用来替换需要动态配置的属性值。比如:

    <dataSource type="POOLED">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    </dataSource>
    

    这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。

    属性也可以被传递到 SqlSessionFactoryBuilder.build()方法中。例如:

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
    // ... or ...
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
    

    如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
    1)在 properties 元素体内指定的属性首先被读取。
    2)然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
    3)最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
    因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。

    8.2.settings设置

    这里写图片描述

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    

    测试:
    没有开启驼峰匹配:

    2018-07-01 13:57:56,486 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 13:57:56,524 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 13:57:56,568 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='null', password='123456', name='大神', age=20, sex=2, birthday='2018-07-01', created='2018-07-01 13:36:09.0', updated='2018-07-01 13:36:09.0'}
    

    开启驼峰匹配:

    2018-07-01 13:58:40,599 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 13:58:40,642 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 13:58:40,661 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='大神', age=20, sex=2, birthday='2018-07-01', created='2018-07-01 13:36:09.0', updated='2018-07-01 13:36:09.0'}
    
    

    8.3.typeAliases

    类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

    <typeAliases>
        <typeAlias type="com.zpc.mybatis.pojo.User" alias="User"/>
    </typeAliases>
    

    缺点:每个pojo类都要去配置。
    解决方案:使用扫描包,扫描指定包下的所有类,扫描之后的别名就是类名(不区分大小写),建议使用的时候和类名一致。

    <typeAliases>
        <!--type:实体类的全路径。alias:别名,通常首字母大写-->
        <!--<typeAlias type="com.zpc.mybatis.pojo.User" alias="User"/>-->
        <package name="com.zpc.mybatis.pojo"/>
    </typeAliases>
    

    Mybatis已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的.
    这里写图片描述

    8.4.typeHandlers(类型处理器)

    无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。

    8.5.plugins(插件)拦截器

    MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
    Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    ParameterHandler (getParameterObject, setParameters)
    ResultSetHandler (handleResultSets, handleOutputParameters)
    StatementHandler (prepare, parameterize, batch, update, query)

    现在一些MyBatis 插件比如PageHelper都是基于这个原理,有时为了监控sql执行效率,也可以使用插件机制
    原理:
    这里写图片描述
    自定义拦截器:

    // ExamplePlugin.java
    @Intercepts({@Signature(
      type= Executor.class,
      method = "update",
      args = {MappedStatement.class,Object.class})})
    public class ExamplePlugin implements Interceptor {
      public Object intercept(Invocation invocation) throws Throwable {
        return invocation.proceed();
      }
      public Object plugin(Object target) {
        return Plugin.wrap(target, this);
      }
      public void setProperties(Properties properties) {
      }
    }
    

    配置:

    <!-- mybatis-config.xml -->
    <plugins>
      <plugin interceptor="org.mybatis.example.ExamplePlugin">
        <property name="someProperty" value="100"/>
      </plugin>
    </plugins>
    

    上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。

    8.6.environments(环境)

    MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
    尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
    虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。

    8.7.mappers

    需要告诉 MyBatis 到哪里去找到 SQL 映射语句。即告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

    <!-- 使用相对于类路径的资源引用 -->
    <mappers>
      <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
      <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
      <mapper resource="org/mybatis/builder/PostMapper.xml"/>
    </mappers>
    
    <!-- 使用映射器接口实现类的完全限定类名 -->
    <mappers>
      <mapper class="org.mybatis.builder.AuthorMapper"/>
      <mapper class="org.mybatis.builder.BlogMapper"/>
      <mapper class="org.mybatis.builder.PostMapper"/>
    </mappers>
    

    这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper。类名,也叫做mapper
    1、定义一个接口。
    2、在接口所在的包中定义mapper.xml,并且要求xml文件和interface的名称要相同。
    3、在mybatis-config.xml 中通过class路径,引入mapper(注解方式)。要求mapper.xml 中的名称空间是类的接口的全路径。

    注解方式:

    <mappers>
        <mapper resource="mappers/MyMapper.xml"/>
        <mapper resource="mappers/UserDaoMapper.xml"/>
        <!--注解方式可以使用如下配置方式-->
        <mapper class="com.zpc.mybatis.dao.UserMapper"/>
    </mappers>
    

    问题:
    1、mapper.xml 和 java文件没有分离。 之后的教程讲述和spring整合之后解决。
    2、需要一个一个的去加载mapper。

    当然也可以使用包扫描(必须使用注解方式,即在接口方法上使用注解,如@Select("select * from tb_user ")):

    缺点:
    1、如果包的路径有很多?
    2、mapper.xml和mapper.java没有分离。
    spring整合的时候解决。

    9.Mapper XML文件详解

    9.1.CRUD标签

    9.1.1.select

    select – 书写查询sql语句
    select中的几个属性说明:
    id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
    resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)
    parameterType:传入参数类型。可以省略

    9.1.2.insert

    insert 的几个属性说明:
    id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
    parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
    useGeneratedKeys:开启主键回写
    keyColumn:指定数据库的主键
    keyProperty:主键对应的pojo属性名
    标签内部:具体的sql语句。

    9.1.3.update

    id属性:当前名称空间下的statement的唯一标识(必须属性);
    parameterType:传入的参数类型,可以省略。
    标签内部:具体的sql语句。

    9.1.4.delete

    delete 的几个属性说明:
    id属性:当前名称空间下的statement的唯一标识(必须属性);
    parameterType:传入的参数类型,可以省略。
    标签内部:具体的sql语句。

    9.2.#{}和${}

    场景:数据库有两个一模一样的表。历史表,当前表
    查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。
    希望使用1个方法来完成操作。

    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from #{tableName}
    </select>
    
    /**
     * 根据表名查询用户信息(直接使用注解指定传入参数名称)
     *
     * @param tableName
     * @return
     */
    public List<User> queryUserByTableName(String tableName);
    

    测试输出:
    这里写图片描述
    有问题,报语法错误:相当于执行了这样一条sql:
    select * from “tb_user”;
    显然表名多了引号。

    改正:

    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from ${tableName}
    </select>
    

    注意:
    #{} 只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。
    ${} 是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。

    使用${} 去取出参数值信息,需要使用${value}
    #{} 只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。

    推荐:

    /**
     * 根据表名查询用户信息(直接使用注解指定传入参数名称)
     *
     * @param tableName
     * @return
     */
    public List<User> queryUserByTableName(@Param("tableName") String tableName);
    
    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from ${tableName}
    </select>
    

    #{}多个参数时:

    /**
     * 登录(直接使用注解指定传入参数名称)
     *
     * @param userName
     * @param password
     * @return
     */
    public User login( String userName, String password);
    
    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>
    

    报错:

    org.apache.ibatis.exceptions.PersistenceException: 
    ### Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
    ### Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
    

    解决方案一:

    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{0} and password = #{1}
    </select>
    

    解决方案二:

    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{param1} and password = #{param2}
    </select>
    

    最终解决方案:

    /**
     * 登录(直接使用注解指定传入参数名称)
     *
     * @param userName
     * @param password
     * @return
     */
    public User login(@Param("userName") String userName, @Param("password") String password);
    
    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>
    

    通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过${“xxxx”}或#{“xxxx”}
    sql语句动态生成的时候,使用${};
    sql语句中某个参数进行占位的时候#{}

    9.3.面试题(#、$区别)

    /**
     * #号
     * @param username1
     * @return
     */
    User queryUserListByName1(@Param("username1") String username1);
    
    /**
     * $号
     * @param username2
     * @return
     */
    User queryUserListByName2(@Param("username2") String username2);
    
    <select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user WHERE user_name=#{username1}
    </select>
    
    <select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user WHERE user_name='${username2}'//手动加了引号
    </select>
    

    9.4.resultMap

    这里写图片描述
    这里写图片描述
    使用:
    这里写图片描述

    9.5.sql片段

    <sql id=””></sql>
    <include refId=”” />
    

    例如在UserMapper.xml中定义如下片段:

    <sql id="commonSql">
    		id,
    			user_name,
    			password,
    			name,
    			age,
    			sex,
    			birthday,
    			created,
    			updated	
    </sql> 
    

    则可以在UserMapper.xml中使用它:

    <select id="queryUserById" resultMap="userResultMap">
    	select <include refid="commonSql"></include> from tb_user where id = #{id}
    </select>
    
    <select id="queryUsersLikeUserName" resultType="User">
    	select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
    </select>
    

    Sql片段也可以定义在单独的.xml文件中如:
    定义CommonSQL.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="CommonSQL">
    	<sql id="commonSql">
    		id,
    			user_name,
    			password,
    			name,
    			age,
    			sex,
    			birthday,
    			created,
    			updated	
    	</sql>
    </mapper>
    

    使用:

    	<select id="queryUserById" resultMap="userResultMap">
    		select <include refid="CommonSQL.commonSql"></include> from tb_user where id = #{id}
    	</select>
    	
    	<select id="queryUsersLikeUserName" resultType="User">
    		select <include refid="CommonSQL.commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
    	</select>
    

    当然要完成这个功能还需要在全局配置文件mybatis-config.xml中引入该外部配置文件:

    <mappers>
    		<mapper resource="CommonSQL.xml"/>
    		<!-- 开启mapper接口的包扫描,基于class的配置方式 -->
    		<package name="com.zpc.mybatis.mapper"/>
    </mappers>
    
    

    10.动态sql

    场景:查询男性用户,如果输入了姓名,按姓名模糊查询
    这里写图片描述

    10.1.if

    场景:查询男性用户,如果输入了姓名,则按姓名查询

    定义接口:

    /**
     * 查询男性用户,如果输入了姓名,则按姓名查询
     * @param name
     * @return
     */
    List<User> queryUserList(@Param("name") String name);
    

    编写mapper

    <select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user WHERE sex=1
        <if test="name!=null and name.trim()!=''">
          and name like '%${name}%'
        </if>
    </select>
    

    测试

    @Test
    public void testqueryUserList() {
        List<User> users = this.userMapper.queryUserList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }
    

    10.2.choose when otherwise

    场景:查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。

    定义接口:

    /**
     * 查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。
     * @param name
     * @param age
     * @return
     */
    List<User> queryUserListByNameOrAge(@Param("name") String name,@Param("age") Integer age);
    

    编写mapper配置:

    <select id="queryUserListByNameOrAge" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user WHERE sex=1
        <!--
        1.一旦有条件成立的when,后续的when则不会执行
        2.当所有的when都不执行时,才会执行otherwise
        -->
        <choose>
            <when test="name!=null and name.trim()!=''">
                and name like '%${name}%'
            </when>
            <when test="age!=null">
                and age = #{age}
            </when>
            <otherwise>
                and name='鹏程'
            </otherwise>
        </choose>
    </select>
    

    测试:

    @Test
    public void queryUserListByNameOrAge() throws Exception {
        List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
        for (User user : users) {
            System.out.println(user);
        }
    }
    

    10.3.where 和set

    场景一:查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。

    接口:

    /**
     * 查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立
     * @param name
     * @param age
     * @return
     */
    List<User> queryUserListByNameAndAge(@Param("name") String name,@Param("age") Integer age);
    

    配置:

    <select id="queryUserListByNameAndAge" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user
        <!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
        <where>
            <if test="name!=null and name.trim()!=''">
                and name like '%${name}%'
            </if>
            <if test="age!=null">
                and age = #{age}
            </if>
        </where>
    </select>
    

    测试:

    @Test
    public void queryUserListByNameAndAge() throws Exception {
        List<User> users = this.userMapper.queryUserListByNameAndAge("鹏程", 20);
        for (User user : users) {
            System.out.println(user);
        }
    }
    

    场景二:修改用户信息,如果参数user中的某个属性为null,则不修改。
    接口:

    /**
     * 根据id更新用户信息
     *
     * @param user
     */
    public void updateUser(User user);
    

    配置:

    <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>
    

    测试:

    @Test
    public void testUpdateUser() {
        User user = new User();
        user.setBirthday(new Date());
        user.setName("静静");
        user.setPassword("123456");
        user.setSex(0);
        user.setUserName("Jinjin");
        user.setId("1");
        this.userMapper.updateUser(user);
    }
    

    10.4.foreach

    场景:按照多个id查询用户信息

    接口:

    /**
     * 按多个Id查询
     * @param ids
     * @return
     */
    List<User> queryUserListByIds(@Param("ids") String[] ids);
    

    配置:

    <select id="queryUserListByIds" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>
    

    测试:

    @Test
    public void queryUserListByIds() throws Exception {
        List<User> users = this.userMapper.queryUserListByIds(new String[]{"1","2"});
        for (User user : users) {
            System.out.println(user);
        }
    }
    

    If:testognl表达式或者简单java代码
    Choose when otherwise—>相当于if else if else
    When test参考if
    Where set 都有一定的纠错功能
    Trim:prefix suffix prefixOverrides suffixOverrides
    Foreach:collection item saparator open close

    11.缓存

    11.1.一级缓存

    这里写图片描述
    mybatis中,一级缓存默认是开启的,并且一直无法关闭

    一级缓存满足条件:
    1、同一个session中
    2、相同的SQL和参数

    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        System.out.println(this.userMapper.queryUserById("1"));
    }
    
    2018-07-01 17:08:50,156 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:08:50,421 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:08:50,423 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:08:50,476 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:08:50,509 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    

    使用:sqlSession.clearCache();可以强制清除缓存

    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        sqlSession.clearCache();
        System.out.println(this.userMapper.queryUserById("1"));
    }
    

    日志:

    2018-07-01 17:10:51,065 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:10:51,359 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:10:51,360 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:10:51,408 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:10:51,437 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:10:51,440 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    

    执行update、insert、delete的时候,会清空缓存
    测试:

    @Test
    public void testQueryUserById() {
        System.out.println(this.userMapper.queryUserById("1"));
        //sqlSession.clearCache();
    
        User user=new User();
        user.setName("美女");
        user.setId("1");
        userMapper.updateUser(user);
    
        System.out.println(this.userMapper.queryUserById("1"));
    }
    

    日志:

    2018-07-01 17:18:15,128 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:18:15,399 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
    2018-07-01 17:18:15,401 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:18:15,466 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:18:15,492 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
    2018-07-01 17:18:15,527 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==>  Preparing: UPDATE tb_user set name = ?, updated = now() WHERE (id = ?); 
    2018-07-01 17:18:15,529 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Parameters: 美女(String), 1(String)
    2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] <==    Updates: 1
    2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:18:15,533 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:18:15,538 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
    

    11.2.二级缓存

    mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。

    开启二级缓存:

    <mapper namespace="com.zpc.mybatis.dao.UserMapper">
        <cache/>
    </mapper>
    

    测试:

    @Test
    public void testCache() {
        System.out.println(this.userMapper.queryUserById("1"));
    
        sqlSession.close();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        System.out.println(mapper.queryUserById("1"));
    }
    

    开启二级缓存,必须序列化:

    public class User implements Serializable{
        private static final long serialVersionUID = -3330851033429007657L;
    

    日志:

    2018-07-01 17:23:39,335 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
    2018-07-01 17:23:39,664 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2092769598.
    2018-07-01 17:23:39,665 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==>  Preparing: select * from tb_user where id = ? 
    2018-07-01 17:23:39,712 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
    2018-07-01 17:23:39,734 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <==      Total: 1
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
    2018-07-01 17:23:39,743 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cbd213e]
    2018-07-01 17:23:39,744 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2092769598 to pool.
    2018-07-01 17:23:39,746 [main] [com.zpc.mybatis.dao.UserMapper]-[DEBUG] Cache Hit Ratio [com.zpc.mybatis.dao.UserMapper]: 0.5
    User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
    

    关闭二级缓存:
    不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
    这里写图片描述

    <settings>
        <!--开启驼峰匹配-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启二级缓存,全局总开关,这里关闭,mapper中开启了也没用-->
        <setting name="cacheEnabled" value="false"/>
    </settings>
    

    这里写图片描述

    12.高级查询

    12.1.表关系说明

    这里写图片描述
    创建order表:
    CREATE TABLE tb_order (
    id int(11) NOT NULL AUTO_INCREMENT,
    user_id int(11) DEFAULT NULL,
    order_number varchar(255) DEFAULT NULL,
    create datetime DEFAULT NULL,
    updated datetime DEFAULT NULL,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

    public class Order {
        private Integer id;
        private Long userId;
        private String orderNumber;
        private Date created;
        private Date updated;
    }
    

    需求说明:
    这里写图片描述

    12.2.一对一查询

    方法一:核心思想扩展Order对象,来完成映射
    新建OrderUser实体类继承Order:

    public class OrderUser extends Order {
        private String userName;
        private String password;
        private String name;
        private Integer age;
        private Integer sex;
        private Date birthday;
        private Date created;
        private Date updated;
    }
    

    OrderMapper接口:

    public interface OrderMapper {
         OrderUser queryOrderUserByOrderNumber(@Param("number") String number);
    }
    

    配置OrderMapper:

     <mapper namespace="com.zpc.mybatis.dao.OrderMapper">
        <select id="queryOrderUserByOrderNumber" resultType="com.zpc.mybatis.pojo.OrderUser">
          select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
       </select>
    </mapper>
    

    测试:

    @Test
    public void queryOrderUserByOrderNumber() throws Exception {
        OrderUser orderUser = orderMapper.queryOrderUserByOrderNumber("201807010001");
        System.out.println(orderUser);
    }
    

    方法二:面向对象的思想,在Order对象中添加User对象。

    在Order对象中添加User属性:

    public class Order {
        private Integer id;
        private Long userId;
        private String orderNumber;
        private Date created;
        private Date updated;
        private User user;
    }
    

    接口:

    /**
     * 根据订单号查询订单用户的信息
     * @param number
     * @return
     */
    Order queryOrderWithUserByOrderNumber(@Param("number") String number);
    

    使用resultType不能完成自动映射,需要手动完成结果集映射resultMap:

     <resultMap id="OrderUserResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
         <id column="id" property="id"/>
         <!--association:完成子对象的映射-->
         <!--property:子对象在父对象中的属性名-->
         <!--javaType:子对象的java类型-->
         <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
         <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
             <id column="user_id" property="id"/>
         </association>
     </resultMap>
    
     <select id="queryOrderWithUserByOrderNumber" resultMap="OrderUserResultMap">
       select * from tb_order o left join tb_user u on o.user_id=u.id where o.order_number = #{number}
    </select>
    

    测试:

    @Test
    public void queryOrderWithUserByOrderNumber() throws Exception {
        Order order = orderMapper.queryOrderWithUserByOrderNumber("201807010001");
        System.out.println(order.getUser());
    }
    

    12.3.一对多查询

    一对多查询:查询订单,查询出下单人信息并且查询出订单详情。

    Order类:

    public class Order {
        private Integer id;
        private Long userId;
        private String orderNumber;
        private Date created;
        private Date updated;
        private User user;
        private List<OrderDetail> detailList;
    }
    
    public class OrderDetail {
        private Integer id;
        private Integer orderId;
        private Double totalPrice;
        private Integer status;
    }
    

    接口:

    /**
     * 根据订单号查询订单用户的信息及订单详情
     * @param number
     * @return
     */
    Order queryOrderWithUserAndDetailByOrderNumber(@Param("number") String number);
    

    Mapper映射:

    <resultMap id="OrderUserDetailResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
        <id column="id" property="id"/>
        <!--collection:定义子对象集合映射-->
        <!--association:完成子对象的映射-->
        <!--property:子对象在父对象中的属性名-->
        <!--javaType:子对象的java类型-->
        <!--autoMapping:完成子对象的自动映射,若开启驼峰,则按驼峰匹配-->
        <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
            <id column="user_id" property="id"/>
        </association>
        <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">
            <id column="id" property="id"/>
        </collection>
    </resultMap>
    
     <select id="queryOrderWithUserAndDetailByOrderNumber" resultMap="OrderUserDetailResultMap">
       select * from tb_order o
       left join tb_user u on o.user_id=u.id
       left join tb_orderdetail od on o.id=od.order_id
       where o.order_number = #{number}
    </select>
    

    测试:

    @Test
    public void queryOrderWithUserAndDetailByOrderNumber() throws Exception {
        Order order = orderMapper.queryOrderWithUserAndDetailByOrderNumber("201807010001");
        System.out.println(order.getUser());
        System.out.println(order.getDetailList());
    }
    

    12.4.多对多查询

    多对多查询:查询订单,查询出下单人信息并且查询出订单详情中的商品数据。

    OrderDetail类

    public class OrderDetail {
        private Integer id;
        private Integer orderId;
        private Double totalPrice;
        private Integer status;
        private Item item;
    }
    
    public class Item {
        private Integer id;
        private String itemName;
        private Float itemPrice;
        private String itemDetail;
    }
    

    接口:

    /**
     * 根据订单号查询订单用户的信息及订单详情及订单详情对应的商品信息
     * @param number
     * @return
     */
    Order queryOrderWithUserAndDetailItemByOrderNumber(@Param("number") String number);
    

    Mapper配置:

    <resultMap id="OrderUserDetailItemResultMap" type="com.zpc.mybatis.pojo.Order" autoMapping="true">
        <id column="id" property="id"/>
        <association property="user" javaType="com.zpc.mybatis.pojo.User" autoMapping="true">
            <id column="user_id" property="id"/>
        </association>
        <collection property="detailList" javaType="List" ofType="com.zpc.mybatis.pojo.OrderDetail" autoMapping="true">
            <id column="detail_id" property="id"/>
            <association property="item" javaType="com.zpc.mybatis.pojo.Item" autoMapping="true">
                <id column="item_id" property="id"/>
            </association>
        </collection>
    </resultMap>
    
     <select id="queryOrderWithUserAndDetailItemByOrderNumber" resultMap="OrderUserDetailItemResultMap">
       select * ,od.id as detail_id from tb_order o
       left join tb_user u on o.user_id=u.id
       left join tb_orderdetail od on o.id=od.order_id
       left join tb_item i on od.item_id=i.id
       where o.order_number = #{number}
    </select>
    

    测试:

    @Test
    public void queryOrderWithUserAndDetailItemByOrderNumber() throws Exception {
        Order order = orderMapper.queryOrderWithUserAndDetailItemByOrderNumber("201807010001");
        System.out.println(order);
        System.out.println(order.getUser());
        System.out.println(order.getDetailList());
    }
    

    至此,目录结构如下:
    这里写图片描述
    数据库脚本:
    CREATE TABLE tb_order (
    id int(11) NOT NULL AUTO_INCREMENT,
    user_id int(11) DEFAULT NULL,
    order_number varchar(255) DEFAULT NULL,
    create datetime DEFAULT NULL,
    updated datetime DEFAULT NULL,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    INSERT INTO tb_order VALUES (‘1’, ‘2’, ‘201807010001’, ‘2018-07-01 19:38:35’, ‘2018-07-01 19:38:40’);

    CREATE TABLE tb_item (
    id int(11) NOT NULL,
    itemName varchar(255) DEFAULT NULL,
    itemPrice decimal(10,2) DEFAULT NULL,
    itemDetail varchar(255) DEFAULT NULL,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    INSERT INTO tb_item VALUES (‘1’, ‘袜子’, ‘29.90’, ‘香香的袜子’);
    INSERT INTO tb_item VALUES (‘2’, ‘套子’, ‘99.99’, ‘冈本001’);

    CREATE TABLE tb_orderdetail (
    id int(11) NOT NULL AUTO_INCREMENT,
    order_id int(11) DEFAULT NULL,
    total_price decimal(10,0) DEFAULT NULL,
    item_id int(11) DEFAULT NULL,
    status int(10) unsigned zerofill DEFAULT NULL COMMENT ‘0成功非0失败’,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    INSERT INTO tb_orderdetail VALUES (‘1’, ‘1’, ‘10000’, ‘1’, ‘0000000001’);
    INSERT INTO tb_orderdetail VALUES (‘2’, ‘1’, ‘2000’, ‘2’, ‘0000000000’);

    12.5.resultMap的继承

    这里写图片描述

    12.6.高级查询的整理

    resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
    type 结果集对应的数据类型 id 唯一标识,被引用的时候,进行指定

    <resultMap type="Order" id="orderUserLazyResultMap">
    <!—定义pojo中的单个对象的 property 定义对象的属性名, javaType 属性的类型,
    		<association property="user" javaType="User" autoMapping="true">
    			<id />
    		</association>
    <!—如果属性是集合使用collection ,javaType 集合的类型,ofType 表示集中的存储的元素类型
    		<collection property="details" javaType="List" ofType="OrderDetail" autoMapping="true">
    			<id />
    </resultMap>
    

    13.延迟加载

    这里写图片描述
    这里写图片描述
    编写接口:
    这里写图片描述
    Mapper配置:
    这里写图片描述
    测试:
    这里写图片描述
    结果:
    这里写图片描述

    开启延迟加载:
    这里写图片描述
    修改测试用例:
    这里写图片描述
    执行,报错:
    这里写图片描述
    添加cglib:

    <dependency>
    		<groupId>cglib</groupId>
    		<artifactId>cglib</artifactId>
    		<version>3.1</version>
    	</dependency>
    

    执行:
    这里写图片描述

    14.如果sql语句中出现’<’的解决方案

    1、使用xml中的字符实体

    这里写图片描述
    因为业务,需要在mybatis中,使用到大于号,小于号,所以就在SQL中直接使用了。
    SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE
    可是,在执行时,总报错误:
    ```Error creating document instance. Cause: org.xml.sax.SAXParseException; lineNumber: 74; columnNumber: 17; ``元素内容必须由格式正确的字符数据或标记组成。
    把AND start_date >= CURRENT_DATE AND end_date <= CURRENT_DATE去掉,就没有问题,所以确定是因为大于号,小于号引起的问题。

    于是就想到了特殊符号,于是用了转义字符把>和<替换掉,然后就没有问题了。
    SELECT * FROM test WHERE 1 = 1 AND start_date &lt;= CURRENT_DATE AND end_date &gt;= CURRENT_DATE
    案例:

    1.<if test="startDateTime!=null"> and mm.ttime &gt; to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss')</if>  
    2.<if test="endDateTime!=null"> and mm.ttime &lt;= to_date(#{endDateTime},'yyyy-mm-dd hh24:mi:ss')</if>  
    

    2、使用<![CDATA[ < ]]>

    案例1:

    1.<![CDATA[ 
    2.       and mm.ttime > to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss') 
    3.       and mm.ttime <= to_date(#{endDateTime},'yyyy-mm-dd hh24:mi:ss') 
    4.]]>  
    

    案例2:

    mapper文件示例代码 :

    and (t1.status <![CDATA[ >= ]]> 1  and  t1.status <![CDATA[ <= ]]> 2)
    上述代码其实对应的sql:
    and (t1.status > =1 andt1.status <= 2)
    

    注意:

    使用<![CDATA[ ]]>标记的sql语句中的<where> <if>等标签不会被解析。
    
    欢迎关注公众号「程猿薇茑」

    15.Spring 集成Mybatis

    15.1引入spring和Mybatis相关依赖

    pom.xml

    <!--数据库连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.8</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.1.3.RELEASE</version>
    </dependency>
    <!--spring集成Junit测试-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.1.3.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <!--spring容器-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.1.3.RELEASE</version>
    </dependency>
    
    

    15.2配置spring配置文件

    applicationContext-dao.xml

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
    
        <!-- 加载配置文件 -->
        <context:property-placeholder location="classpath:properties/*.properties"/>
        <!-- 数据库连接池 -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
              destroy-method="close">
            <property name="driverClassName" value="${jdbc.driver}"/>
            <property name="url"
                      value="jdbc:mysql://${jdbc.host}:3306/${jdbc.database}?useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull"/>
            <property name="username" value="${jdbc.userName}"/>
            <property name="password" value="${jdbc.passWord}"/>
            <!-- 初始化连接大小 -->
            <property name="initialSize" value="${jdbc.initialSize}"></property>
            <!-- 连接池最大数据库连接数  0 为没有限制 -->
            <property name="maxActive" value="${jdbc.maxActive}"></property>
            <!-- 连接池最大的空闲连接数,这里取值为20,表示即使没有数据库连接时依然可以保持20空闲的连接,而不被清除,随时处于待命状态 0 为没有限制 -->
            <property name="maxIdle" value="${jdbc.maxIdle}"></property>
            <!-- 连接池最小空闲 -->
            <property name="minIdle" value="${jdbc.minIdle}"></property>
            <!--最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制-->
            <property name="maxWait" value="${jdbc.maxWait}"></property>
        </bean>
    
        <!-- spring和MyBatis完美整合 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!-- 自动扫描mapping.xml文件 -->
            <property name="mapperLocations" value="classpath:mappers/*.xml"></property>
            <!--如果mybatis-config.xml没有特殊配置也可以不需要下面的配置-->
            <property name="configLocation" value="classpath:mybatis-config.xml" />
        </bean>
    
        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.zpc.mybatis.dao"/>
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
        </bean>
    
        <!-- (事务管理)transaction manager -->
        <bean id="transactionManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>
    

    db.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.host=localhost
    jdbc.database=ssmdemo
    jdbc.userName=root
    jdbc.passWord=123456
    jdbc.initialSize=0
    jdbc.maxActive=20
    jdbc.maxIdle=20
    jdbc.minIdle=1
    jdbc.maxWait=1000
    

    由于applicationContext-dao.xml中配置了Mapper接口扫描,所以删除mybatis-config.xml中的配置,否则报已映射错误:
    Caused by: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for MyMapper.selectUser
    删除mybatis-config.xml中的映射配置:

    <!--<mappers>-->
        <!--<mapper resource="mappers/MyMapper.xml"/>-->
        <!--<mapper resource="mappers/UserDaoMapper.xml"/>-->
        <!--<mapper resource="mappers/UserMapper.xml"/>-->
        <!--<mapper resource="mappers/OrderMapper.xml"/>-->
    <!--</mappers>-->
    

    或者在构建sqlSessionFactory时不配置mybatis-config.xml也行:

    <!-- spring和MyBatis完美整合 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:mappers/*.xml"></property>
        <!--如果mybatis-config.xml没有特殊配置也可以不需要下面的配置-->
        <!--<property name="configLocation" value="classpath:mybatis-config.xml" />-->
    </bean>
    

    15.3 测试

    UserMapperSpringTest.java

    import com.zpc.mybatis.dao.UserMapper;
    import com.zpc.mybatis.pojo.User;
    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 org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import java.io.InputStream;
    import java.util.Date;
    import java.util.List;
    
    //目标:测试一下spring的bean的某些功能
    @RunWith(SpringJUnit4ClassRunner.class)//junit整合spring的测试//立马开启了spring的注解
    @ContextConfiguration(locations="classpath:spring/applicationContext-*.xml")//加载核心配置文件,自动构建spring容器
    public class UserMapperSpringTest {
    
        @Autowired
        private UserMapper userMapper;
    
        @Test
        public void testQueryUserByTableName() {
            List<User> userList = this.userMapper.queryUserByTableName("tb_user");
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testLogin() {
            System.out.println(this.userMapper.login("hj", "123456"));
        }
    
        @Test
        public void testQueryUserById() {
            System.out.println(this.userMapper.queryUserById("1"));
            User user = new User();
            user.setName("美女");
            user.setId("1");
            userMapper.updateUser(user);
    
            System.out.println(this.userMapper.queryUserById("1"));
        }
    
        @Test
        public void testQueryUserAll() {
            List<User> userList = this.userMapper.queryUserAll();
            for (User user : userList) {
                System.out.println(user);
            }
        }
    
        @Test
        public void testInsertUser() {
            User user = new User();
            user.setAge(20);
            user.setBirthday(new Date());
            user.setName("大神");
            user.setPassword("123456");
            user.setSex(2);
            user.setUserName("bigGod222");
            this.userMapper.insertUser(user);
            System.out.println(user.getId());
        }
    
        @Test
        public void testUpdateUser() {
            User user = new User();
            user.setBirthday(new Date());
            user.setName("静静");
            user.setPassword("123456");
            user.setSex(0);
            user.setUserName("Jinjin");
            user.setId("1");
            this.userMapper.updateUser(user);
        }
    
        @Test
        public void testDeleteUserById() {
            this.userMapper.deleteUserById("1");
        }
    
        @Test
        public void testqueryUserList() {
            List<User> users = this.userMapper.queryUserList(null);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void queryUserListByNameAndAge() throws Exception {
            List<User> users = this.userMapper.queryUserListByNameAndAge("鹏程", 20);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void queryUserListByNameOrAge() throws Exception {
            List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
            for (User user : users) {
                System.out.println(user);
            }
        }
    
        @Test
        public void queryUserListByIds() throws Exception {
            List<User> users = this.userMapper.queryUserListByIds(new String[]{"5", "2"});
            for (User user : users) {
                System.out.println(user);
            }
        }
    
    

    目录结构:
    这里写图片描述

    16.SpringBoot 集成Mybatis

    请参见博文:https://blog.csdn.net/hellozpc/article/details/82531834

    17.Mybatis Generator的使用

    • MyBatis Generator(MBG)是MyBatis 和iBATIS的代码生成器。可以生成简单CRUD操作的XML配置文件、Mapper文件(DAO接口)、实体类。实际开发中能够有效减少程序员的工作量,甚至不用程序员手动写sql。
      mybatis-generator有多种用法:命令行、maven插件等。命令行方式通常要把相关jar包下载到本地,再使用java -jar 运行。方便起见,本文演示使用maven插件的方式。

    1.新建一个Maven项目(可以直接建立一个初始的springboot项目)
    pom文件引入mybatis-generator-maven-plugin
    今天失恋了

    <!-- mybatis-generator自动生成代码插件 -->
    <plugin>
       <groupId>org.mybatis.generator</groupId>
       <artifactId>mybatis-generator-maven-plugin</artifactId>
       <version>1.3.5</version>
    </plugin>
    

    2.将插件需要的配置文件拷入到resource目录下,并做配置
    在这里插入图片描述
    generator.properties:配置数据库信息,在generatorConfig.xml使用:

    #generatorConfig Info
    generator.location=D:\\software\\maven\\apache-maven-3.3.9\\repository\\mysql\\mysql-connector-java\\5.1.32\\mysql-connector-java-5.1.32.jar
    generator.targetPackage=com.zpc.videoshow.generated
    #gererator.schema=oracle-schema
    gererator.tableName=video_info
    gererator.domainObjectName=VideoInfo
    
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.host=jdbc:mysql://localhost:3306/videoshow
    jdbc.userName=root
    jdbc.passWord=123456
    jdbc.initialSize=0
    jdbc.maxActive=20
    jdbc.maxIdle=20
    jdbc.minIdle=1
    jdbc.maxWait=1000
    

    generatorConfig.xml:配置generator插件运行需要的参数信息

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
        <!-- 引入配置文件 -->
        <properties resource="generator.properties"/>
        <!-- 数据库驱动包位置,路径请不要有中文-->
        <!-- <classPathEntry location="D:\software\lib\mysql-connector-java-5.1.21.jar" /> -->
        <classPathEntry location="${generator.location}"/>
        <!-- 一个数据库一个context-->
        <context id="DB2Tables" targetRuntime="MyBatis3">
            <!-- 生成的pojo,将implements Serializable -->
            <plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin>
    
            <!-- 注释 -->
            <commentGenerator>
                <property name="suppressAllComments" value="true"/><!-- 是否取消注释 -->
                <!-- <property name="suppressDate" value="true" />  是否生成注释代时间戳 -->
            </commentGenerator>
    
            <!-- 数据库链接URL、用户名、密码 -->
            <!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/sy" userId="sypro" password="sypro"> -->
            <jdbcConnection driverClass="${jdbc.driver}" connectionURL="${jdbc.host}" userId="${jdbc.userName}"
                            password="${jdbc.passWord}">
            </jdbcConnection>
    
            <!-- 类型转换 -->
            <javaTypeResolver>
                <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL
                    和 NUMERIC 类型解析为java.math.BigDecimal -->
                <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
    
            <!-- 生成model模型,设置对应的包名(targetPackage)和存放路径(targetProject)。targetProject可以指定具体的路径,如./src/main/java,也可以使用MAVEN来自动生成,这样生成的代码会在target/generatord-source目录下 -->
            <javaModelGenerator targetPackage="${generator.targetPackage}" targetProject="./src/main/java">
                <!-- 是否在当前路径下新加一层schema,eg:false路径com.oop.eksp.user.model 而true:com.oop.eksp.user.model.[schemaName] -->
                <property name="enableSubPackages" value="false"/>
                <!-- 从数据库返回的值被清理前后的空格 -->
                <property name="trimStrings" value="true"/>
            </javaModelGenerator>
    
            <!--对应的mapper.xml文件 -->
            <sqlMapGenerator targetPackage="${generator.targetPackage}" targetProject="./src/main/java">
                <property name="enableSubPackages" value="true"/>
            </sqlMapGenerator>
    
            <!-- 对应的Mapper接口类文件 -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="${generator.targetPackage}"
                                 targetProject="./src/main/java">
                <property name="enableSubPackages" value="true"/>
            </javaClientGenerator>
    
            <!-- 列出要生成代码的所有表,这里配置的是不生成Example文件 -->
            <!-- schema即为数据库名tableName为对应的数据库表 domainObjectName是要生成的实体类 enable*ByExample是否生成 example类   -->
            <table tableName="${gererator.tableName}" domainObjectName="${gererator.domainObjectName}"
                   schema="${gererator.schema}"
                   enableCountByExample="false" enableUpdateByExample="false"
                   enableDeleteByExample="false" enableSelectByExample="false"
                   selectByExampleQueryId="false">
                <!-- 忽略列,不生成bean 字段
                <ignoreColumn column="FRED" />-->
                <!-- 指定列的java数据类型
                <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />  -->
                <!-- 用于指定生成实体类时是否使用实际的列名作为实体类的属性名。false是 Camel Case风格-->
                <property name="useActualColumnNames" value="false"/>
            </table>
        </context>
    </generatorConfiguration>
    

    3.运行generator插件(确保数据库已经运行)
    方法1:直接找到mybatis-generator的插件,右击运行。
    在这里插入图片描述
    方法2:在运行配置里面添加maven命令
    在这里插入图片描述
    在这里插入图片描述

    4.查看生成的文件
    在这里插入图片描述

    5.一些小技巧
    a) 建表时,字段名称建议用"_"分隔多个单词,比如:AWB_NO、REC_ID…,这样生成的entity,属性名称就会变成漂亮的驼峰命名,即:awbNo、recId

    b)oracle中,数值形的字段,如果指定精度,比如Number(16,2),默认生成entity属性是BigDecimal型 ,如果不指定精度,比如:Number(8),指默认生成的是Long型

    c)oracle中的nvarchar/nvarchar2,mybatis-generator会识别成Object型,建议不要用nvarchar2,改用varchar2

    6.Example文件的使用
    用过Hibernate的同学一定感叹于其完全不用手动写sql的强大功能,其实Mybatis也可以配置生成Example,省去一些简单的sql编写,实际开发中也会带来方便。
    a.修改generatorConfig.xml的配置:

     enableCountByExample="true" enableUpdateByExample="true"
     enableDeleteByExample="true" enableSelectByExample="true"
     selectByExampleQueryId="true"
    

    b.pom中引入mybatis的依赖:

    <dependency>
       <groupId>org.mybatis</groupId>
       <artifactId>mybatis</artifactId>
       <version>3.4.1</version>
    </dependency>
    

    c.运行generator
    这种情况下多生成了一个Example的文件,Mapper文件的内容也会多很多example相关的:
    在这里插入图片描述
    Example的详细使用百度之,参见:
    https://blog.csdn.net/m0_37795198/article/details/78848045

    生成注解版Mapper参考:
    https://blog.csdn.net/hellozpc/article/details/107120406

    18.MyBatis整合分页插件 pageHelper

    请参见博文:https://blog.csdn.net/hellozpc/article/details/82531834

    **欢迎关注公众号**
    **微信扫一扫**
    展开全文
  • Mybatis全面详解——上(学习总结)

    万次阅读 多人点赞 2018-04-18 10:29:11
    一、什么是Mybatis这里借用官网的一句话介绍什么是mybatisMyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis ...

    一、什么是Mybatis

    这里借用官网的一句话介绍什么是mybatis:MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

    二、Mybatis相对JDBC有哪些优势

    首先我们来看一看jdbc连接数据库的连接方法:

    public static void main(String[] args) {
    			Connection connection = null;
    			PreparedStatement preparedStatement = null;
    			ResultSet resultSet = null;
    			
    			try {
    				//1、加载数据库驱动
    				Class.forName("com.mysql.jdbc.Driver");
    				//2、通过驱动管理类获取数据库链接
    				connection =  DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
    				//3、定义sql语句 ?表示占位符
    			        String sql = "select * from user where username = ?";
    				//4、获取预处理statement
    				preparedStatement = connection.prepareStatement(sql);
    				//5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
    				preparedStatement.setString(1, "王五");
    				//6、向数据库发出sql执行查询,查询出结果集
    				resultSet =  preparedStatement.executeQuery();
    				//7、遍历查询结果集
    				while(resultSet.next()){
    					User user 
    					System.out.println(resultSet.getString("id")+"  "+resultSet.getString("username"));
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}finally{
    				//8、释放资源
    				if(resultSet!=null){
    					try {
    						resultSet.close();//释放结果集
    					} catch (SQLException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				if(preparedStatement!=null){
    					try {
    						preparedStatement.close();
    					} catch (SQLException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				if(connection!=null){
    					try {
    						connection.close();//关闭数据库连接
    					} catch (SQLException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    
    			}
    
    		}

    通过上面的一段jdbc连接数据代码,我们看有哪些不好的地方:

    1.在创建connection的时候,存在硬编码问题(也就是直接把连接信息写死,不方便后期维护)

    2.preparedStatement对象在执行sql语句的时候存在硬编码问题。

    3.每次在进行一次数据库连接后都会关闭数据库连接,频繁的开启/关闭数据连接影响性能。

    简单的说一下mybatis相对jdbc的优势:

    1.mybatis是把连接数据库的信息都是写在配置文件中,因此不存在硬编码问题,方便后期维护。

    2.mybatis执行的sql语句都是通过配置文件进行配置,不需要写在java代码中。

    3.mybatis的连接池管理、缓存管理等让连接数据库和查询数据效率更高。

    ........

    三、Mybatis框架的原理介绍

    这里就通过一张图来对mybatis框架原理进行介绍吧:

    四、Mybatis全局配置文件

    SqlMapConfig.xml是Mybatis的全局配置文件,它的名称可以是任意,但是一般命名都为(SqlMapConfig)

    4.1.全局配置文件的类容和顺序

    Properties(属性)

    Settings(全局参数设置)

    typeAliases(类型别名)

    typeHandlers(类型处理器)

    objectFactory(对象工厂)

    plugins(插件)

    environments(环境信息集合)

    environment(单个环境信息)

    transactionManager(事物)

    dataSource(数据源)

    mappers(映射器)

    4.2.常见配置详解

    properties标签:

    Mybatis可以通过该标签来读取java配置信息:

    例如在工程中对数据源信息写在db.properties文件中,可以通过properties标签来加载该文件。

    db.properties:

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

    SqlMapConfig.xml使用properties标签:

    <!-- 通过properties标签,读取java配置文件的内容 -->
    <properties resource="db.properties" />
    
    <!-- 配置mybatis的环境信息 -->
    <environments default="development">
    	<environment id="development">
    		<!-- 配置JDBC事务控制,由mybatis进行管理 -->
    		<transactionManager type="JDBC"></transactionManager>
    		<!-- 配置数据源,采用dbcp连接池 -->
    		<dataSource type="POOLED">
    			<property name="driver" value="${db.driver}"/>
    			<property name="url" value="${db.url}"/>
    			<property name="username" value="${db.username}"/>
    			<property name="password" value="${db.password}"/>
    		</dataSource>
    	</environment>
    </environments>

    注意:

    1、 先加载propertiesproperty标签声明的属性

    因此在property中的name属性的值和value比properties中的resource属性先加载。后加载的db.properties会覆盖于property加载属性和值。

    <properties resource="db.properties">

        <property name="db.username",value="1234"/>

    </properties>  

    2、 再加载properties标签引入的java配置文件中的属性

    3、 parameterType的值会和properties的属性值发生冲突。因此,在properties文件里的内容命名最好加上db.代表是跟数据源相关的属性,这样就不容易跟以后的属性发生冲突。

    settings标签:

    该标签时mybatis的全局设置,该设置会影响mybatis的运行。

    一般我们使用使用该标签来开启二级缓存和懒加载。

    以下是几张settings配置项的说明:



    typeAliases标签

    该标签是对po类进行别名设置,这样,在后面使用po类的时候就可以直接通过别名引用,而不需要通过po类的全限定名来引用。这样可以提高我们的开发效率。

    首先介绍下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

    自定义单个别名:这种方式只能定义单个类的别名。

    下面的代码就是把com.lc.mybatis.po.User类定义为user的别名

    <typeAliases>
    		<!-- 设置单个别名 -->
    		<typeAlias type="com.lc.mybatis.po.User" alias="user"/>
    	</typeAliases>

    自定义之批量定义别名:

    下面代码是把com.lc.mybatis.po类下的所有类都声明别名,默认的别名就是类名(类名大小写都可以)

    <!-- 设置别名 -->
    	<typeAliases>
    		<!-- 批量设置别名 -->
    		<!-- package:指定包名称来为该包下的po类声明别名,默认的别名就是类名(类名首字母大小写都可以) -->
    		<package name="com.lc.mybatis.po"/>
    	</typeAliases>

    mappers标签

    该标签的作用是加载映射文件

    方式一:<mapper resource=""/>

    该方式是加载相对于类路径下的映射文件:

    <mappers>
       <mapper resource="sqlmap/User.xml"/>
    </mappers>

    方式二:<mapper url=""/>

    该方式使用全限定路径

    <mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />

    方式三:<mapper class=""/>

    该方式使用mapper接口的全限定类名

    <mapper class="cn.itcast.lc.mapper.UserMapper"/>

    此方式要求:

        Mapper接口Mapper映射文件名称相同且在同一个目录下。

    方式四:<package name=""/>

        该方式是加载指定包下的所有映射文件

    <package name="cn.lc.mybatis.mapper"/>

    此方式要求:

        Mapper接口Mapper映射文件名称相同且在同一个目录下。

    五、映射文件

    5.1.输入映射parameterType

    第一种:简单类型

    #{}表示占位符?parameterType接收简单类型的参数时里面的名称可以任意

    <select id="findUserById" parameterType="java.lang.Integer" resultType="user">
    		SELECT * FROM USER WHERE id = #{id}
    	</select>

    ${}表示拼接符,parameterType接收简单类型的参数时里面的名称必须value

    <select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
    		SELECT * FROM USER WHERE username LIKE "%${value}%"
    	</select>

    第二种:pojo类型

    这里通过用户的用户名进行模糊查询演示pojo类型

    在映射文件中添加模糊查询语句:

    <!-- parameterType传递pojo类型 -->
    	<select id="findUsersByPojo" parameterType="com.lc.mybatis.po.User" resultType="com.lc.mybatis.po.User">
    		SELECT  * FROM USER WHERE username LIKE "%${username}%"
    	</select>

    user类

    public class User {
    	private Integer id;
    	private String username;
    	private Date birthday;
    	private String sex;
    	private String address;
    }

    测试类:

    public class UserDao{
    //根据用户名进行模糊查询
            @Override
            public List<User> findUserByPojo(){ 
                //全局配置文件路径:
                 String resource = "SqlMapConfig.xml";
                 InputStream inputStream = Resources.getResourceAsStream(resource);
                 //创建SqlSessionFactory
                 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                 SqlSession sqlSession = sqlSessionFactory.openSession();
                 User user = new User();
                 user.setUsetname("张三");
                 List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo",user);//传入pojo
                 System.out.println(users);
                 sqlSession.close();
                 return users;
            }
    }

    第三种:包装类型pojo

    这里通过用户名和用户地址对用户进行查询来演示包装类型pojo:

    首先创建包装pojo类:

    public class UserVO {
    	private User user;
    
    	public User getUser() {
    		return user;
    	}
    
    	public void setUser(User user) {
    		this.user = user;
    	}
    	
    }

    在映射文件中添加查询语句:

    <!-- parameterType传递pojo包装类型 -->
    	<select id="findUsersByPojo1" parameterType="com.lc.mybatis.po.UserVO" resultType="user"> 
    		SELECT * FROM USER WHERE username LIKE "%${user.username}%" AND address=#{user.address}
    	</select>

    测试类:

    public class UserDao{
    //根据用户名和地址进行查询
            @Override
            public List<User> findUserByPojo1(){
                    //全局配置文件路径:
                    String resource = "SqlMapConfig.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //创建SqlSessionFactory
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    User user = new User();
                    user.setUsetname("张三");
                    user.setAddress("郝汉山");
                    UserVO userVo = new UserVO();
                    userVo.setUser(user);
                    List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo1",userVo);//传入pojo包装类
                    System.out.println(users);
                    sqlSession.close();
                    return users;
            }
    
    }

    第四种:map集合类型

    这里通过查询用户信息演示:

    在映射文件中添加该查询语句:

    <!-- parameterType传递hashmap类型 -->
    	<select id="findUsersByMap" parameterType="java.util.Map" resultType="user">
    		SELECT * FROM USER WHERE username LIKE "%${username}%" AND address=#{address}
    	</select>

    测试方法:

    public class UserDao{
    //根据用户名和地址进行查询
            @Override
            public List<User> findUserByMap(){
                    //全局配置文件路径:
                    String resource = "SqlMapConfig.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //创建SqlSessionFactory
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    Map<String,String> map = new HashMap<>();
                    map.put("username","张三");
                    map.put("address","郝汉山");
                    List<User> users = (List<User>) sqlSession.selectList("test.findUsersByMap",map);//传入pojo包装类
                    System.out.println(users);
                    sqlSession.close();
                    return users;
            }
    
    }
    5.2.resultType结果映射

    resultType结果映射要求:需要查询结果的列名和映射的对象的属性名一致,这样才能映射成功。如果映射没成功也不会报错,只是映射结果中对象的相应属性没有值,为空。如果映射的列名和对象中的属性名全部不一致,那么映射的对象为空。如果在使用sql语句查询的时候给查询结果列设置了别名,则别名要和映射结果对象的属性名一致,这样才能保证映射成功。

    第一种:简单类型

    注意: 如果结果映射为简单类型,则需要查询的结果为一列才能映射成功。

    例如:查询用户表中用户的总数。

    映射文件为:

    <!-- resultType:输出为简单类型 -->
    	<select id="findUserCount" resultType="int">
    		select count(*) from user;
    	</select>

    测试代码:

    public class UserDao{
            @Override
            public int findUserCount(){
                    //全局配置文件路径:
                    String resource = "SqlMapConfig.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //创建SqlSessionFactory
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    int userCount= sqlSession.selectOne("test.findUserCount");
                    System.out.println(userCount);
                    sqlSession.close();
                    return userCount;
            }
    
    }

    第二种:pojo结果映射

    这里操作pojo输入映射。

    5.3.resultMap结果映射

    使用resultMap结果映射时,不需要查询出来的结果集的列名和映射结果对象的属性名相同,但是需要声明一个resultMap,手动的方式来对列名和对象属性进行映射。(resultMap一般用于多表关联映射)

    例如:通过查询用户表中的用户,并对查询出来的用户表的列名设置别名。

    映射文件添加查询语句:

    [id]:定义resultMap的唯一标识

    [type]:定义该resultMap最终映射的pojo对象

    [id标签]:映射结果集的唯一标识列,如果是多个字段联合唯一,则定义多个id标签

    [result标签]:映射结果集的普通列

    [column]:SQL查询的列名,如果列有别名,则该处填写别名

    [property]:pojo对象的属性名

    <!-- 如果查询出来的列名有别名就不能通过resultType来接收输出类型了。需要通过resultMap来声明传出类型(resultMap需要声明) -->
    	<resultMap type="user" id="userMap">
    		<!-- id标签:专门查询结果中唯一列映射 -->
    		<id column="id_" property="id"/>
    		<!-- result标签:映射查询结果中的普通列 -->
    		<result column="username_" property="username"/>
    		<result column="address_" property="address"/>
    	</resultMap>
    	<select id="findUserResultMap" parameterType="int" resultMap="userMap">
    		select id id_,username username_,address address_ from user where id=#{id}
    	</select>

    测试类:

    public class UserDao{
            @Override
            public User findUserResultMap(){
                    //全局配置文件路径:
                    String resource = "SqlMapConfig.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //创建SqlSessionFactory
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    User user = sqlSession.selectOne("test.findUserResultMap",1);
                    System.out.println(user);
                    sqlSession.close();
                    return user;
            }
    
    }
    5.4.动态sql

    在mybatis中提供了一些动态sql标签,可以让我们开发效率更快,这些动态sql语句可以增加我们写的sql语句的重用性,常用的动态sql语句标签有:if标签、sql片段(需要先定义后使用)、where标签、foreach标签

    通过下面例子来演示动态sql常用的标签:

    需求分析:查询用户表中相关的用户。

    发出相关的sq语句有:select * from user where username like "%张三%" and address= ?;

                                       select * from user;

                                       select * from user where id in(1,2,10);

    如何才能让这些语句在我们需要的时候就使用,不需要的时候就不适用。其实我们发现这三条语句有很多重复的地方,我们如果能做到让重复的能够重复使用,没有重复的可以按我们需求使用的话就能减少我们写很多sql语句。当然动态sql就是帮我们解决这些问题的。

    映射文件:

    <!-- 定义sql片段,用于可重用的sql片段 -->
    	<sql id="whereClause">
    		<!-- if标签:可以对输入的参数进行判断 -->
    		<!-- test:指定判断表达式 -->
    		<if test="user!=null">
    			<if test="user.username!=null and user.username!=''">
    				AND username LIKE '%${user.username}%'
    			</if>
    			<if test="user.address!=null and user.address!=''">
    				AND address=#{user.address}
    			</if>
    		</if>
    		<if test="idList!=null">
    			AND id IN
    			<!-- foreach标签: 可以循环传入参数值 -->
    			<!-- collenction:标示pojo中集合属性的属性名称 -->
    			<!-- item:每次遍历出来的对象 -->
                            <!--open开始遍历时拼接的串-->
                            <!--close结束遍历时拼接的串-->
                            <!--separator遍历每个对象之间需要拼接字符-->
                            <foreach collection="idList" item="item" open="(" close=")" separator=",">
    				#{item}
    			</foreach>
    		</if>
    	</sql>
    	
    	<select id="findUserList" parameterType="userVO" resultType="user">
    		SELECT * FROM USER
    		<!-- where标签: 默认去掉第一个AND,如果没有参数就去掉where自己-->
                    <where>
                        <!--引用sql语句片段-->
                         <include refid="whereClause"></include>
    		</where>
    	</select>

    userVo类:

    public class UserVO {
    	private User user;
    	private List<Integer> idList;
    	
    
    	public List<Integer> getIdList() {
    		return idList;
    	}
    
    	public void setIdList(List<Integer> idList) {
    		this.idList = idList;
    	}
    
    	public User getUser() {
    		return user;
    	}
    
    	public void setUser(User user) {
    		this.user = user;
    	}
    	
    }


    测试类:

    public class UserDao{
            @Override
            public void findUserList(){
                    //全局配置文件路径:
                    String resource = "SqlMapConfig.xml";
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //创建SqlSessionFactory
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    //测试foreach语句执行
                    UserVO userVo = new UserVO();
                    List<Integer> idList = new ArrayList<>();
                    idList.add(1);
                    idList.add(2);
                    idList.add(3);
                    userVo.setIdList(idList);
                    //测试select * from user where username like ? and address=?;
                    //User user = new User();
                    //user.setUsername("张三");
                    //user.setAddress("武当山");
                    //userVo.setUser(user);
                    List<User> users = sqlSession.selectOne("test.findUserList",userVo);
                    System.out.println(users);
                    sqlSession.close(); 
            }
    
    }

    六、Mybatis之传统Dao层的开发(该方式很少用)

    6.1.开发环境准备:

            1.个人使用的jdk为1.7

            2.开发工具是使用的Eclipse4.5

            3.数据库使用的MySQL5X

            4.导入mybatis所需要的jar包

            

            5.导入mysql数据库驱动包

    6.2.需求分析:

    根据ID查询查询用户、根据用户名称进行模糊查询、添加用户

    6.3.代码实现

    创建用户表对应pojo类User:

    public class User {
    	private Integer id;
    	private String username;
    	private Date birthday;
    	private String sex;
    	private String address;
    	//setter和get方法省略
    }

    创建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>
    	<!-- 配置mybatis的环境信息 -->
    	<environments default="developments">
    		<environment id="developments">
    			<!-- 配置JDBC事务控制,由mybatis进行管理 -->
    			<transactionManager type="JDBC"></transactionManager>
    			<!-- 配置数据源,采用mybatis连接池 -->
    			<dataSource type="POOLED">
    				<property name="driver" value="com.mysql.jdbc.Driver"/>
    				<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
    				<property name="username" value="root"/>
    				<property name="password" value="12345678"/>
    			</dataSource>
    		</environment>
    	</environments>
    	
    	<!-- 加载映射文件 -->
    	<mappers>
    		<mapper resource="User.xml"/>
    	</mappers>
    </configuration>

    创建需求开发的映射文件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进行分类化管理,可以理解为SQL隔离
    	注意:使用mapper代理开发时,namespace有特殊且重要的作用
     -->
    <mapper namespace="test">
    	<!-- 根据用户ID查询用户信息 -->
    	<!-- select:表示一个MappedStatement对象 -->
    	<!-- id:statement的唯一标识 -->
    	<!--  #{}:表示一个占位符?-->
    	<!-- #{id}:里面的id表示输入参数的参数名称,如果该参数为简单类型,那么#{}里面的参数可以任意 -->
    	<!-- parameterType:输入参数的java类型 -->
    	<!-- resultType:输出结果的所映射的java类型(单条结果所对应的java类型) -->
    	<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
    		SELECT * FROM USER WHERE id = #{id}
    	</select>
    	<!-- 根据用户名进行模糊查询 -->
    	<!-- ${}:表示一个sql连接符 -->
    	<!-- ${value}:里面的 value表示输入的名称,如果该参数是简单类型,那么${}里面的参数名称必须是value-->
    	<!-- ${}这种写法存在sql注入的风险,所以需要 慎用!但是有些场景需要用到比如:排序,-->
    	<select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
    		SELECT * FROM USER WHERE username LIKE "%${value}%"
    	</select>
    	
    	<!-- 添加用户: -->
    	<!-- 
    		[selectKey标签]:通过select查询来生成主键
    		[keyProperty]:指定存放生成主键的属性
    		[resultType]:生成主键所对应的Java类型
    		[order]:指定该查询主键SQL语句的执行顺序,相对于insert语句
    		[last_insert_id]:MySQL的函数,要配合insert语句一起使用,该函数在mysql中是执行在insert语句后。
    	-->
    	<insert id="insertUser" parameterType="com.lc.mybatis.po.User">
    		<selectKey keyProperty="id" resultType="int" order="AFTER">
    			SELECT LAST_INSERT_ID()
    		</selectKey>
    	
    		INSERT INTO USER(username,sex,birthday,address) VALUES(#{username},#{sex},#{birthday},#{address})
    	</insert>
    </mapper>

    dao层接口:

    public interface UserDao {
    
    	//根据用户id查询用户
    	public User findUserById(int id);
    
            //根据用户名进行模糊查询
            public List<User> findUsersByName(String name);
    
            //添加用户
            public void addUser(User user);
     }

    dao层的实现:

    public class UserDaoImpl implements UserDao{
            //通过构造方法注入sqlSessionFactory
    	private SqlSessionFactory sqlSessionFactory;
    	
    	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
    		this.sqlSessionFactory = sqlSessionFactory;
    	}
    	
            //根据id查询用户
    	@Override
    	public User findUserById(int id) {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		User user = sqlSession.selectOne("test.findUserById", id);
    		System.out.println(user);
    		sqlSession.close();
    		return user;
    	}
            //根据用户名进行模糊查询
            @Override
            public List<User> findUsersByName(String name){
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                    List<User> users = sqlSession.selectList("test.findUsersByName",name);
                    System.out.println(users);
                    sqlSession.close();
                    return users;
            }
            
    
         //添加用户
        
    
        public List<User> addUser(String name){
                SqlSession sqlSession = sqlSessionFactory.openSession();
                User user = new User();
                user.setUsername("超哥2");
                user.setAddress("成都市");
                //调用SqlSession的增删改查方法
                //第一个参数:表示statement的唯一表示
                //第一个参数:表示占位符的
                sqlSession.insert("test.insertUser", user);
                System.out.println(user.getId());
                //切记:增删改操作需要提交事务。
                sqlSession.commit();
                //关闭资源
                sqlSession.close();
        }


    测试类:这里就只通过根据id查找用户进行测试

    public class UserDaoTest {
    	private SqlSessionFactory sqlSessionFactory;
    	@Before
    	public void setUp() throws IOException {
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	}
    	
    	@Test
    	public void testFindUserById() {
    		UserDao userDao = new UserDaoImpl(sqlSessionFactory);
    		userDao.findUserById(1);
    	}
    
    }

    七、Mybatis之Mapper接口的开发方式

    该方式开发,不需要写dao层的实现类,而是mybatis根据映射文件等信息对接口进行jdk动态代理生成代理类来实现接口中的方法,因此,采用这种方式,我们只需要编辑接口,而不需要去写实现。

    7.1.需求分析

    根据id查询用户。

    7.2.Mapper开发代理规范
    1、mapper接口的全限定名要和mapper映射文件的namespace值一致。
    2、mapper接口的方法名称要和mapper映射文件的statement的id一致。
    3、mapper接口的方法参数类型要和mapper映射文件的statement的parameterType的值一致,而且它的参数是一个。

    4、mapper接口的方法返回值类型要和mapper映射文件的statement的resultType的值一致。

    7.3.代码实现

    准备po类:

    public class User {
    	private Integer id;
    	private String username;
    	private Date birthday;
    	private String sex;
    	private String address;
            //getter和setter方法省略
     }

    Mapper接口:

    public interface UserMapper {
    	
    	public User findUserById(int id);
    }


    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.lc.mybatis.mapper.UserMapper">
    	<!-- 根据用户ID查询用户信息 -->
    	<!-- select:表示一个MappedStatement对象 -->
    	<!-- id:statement的唯一标识 -->
    	<!--  #{}:表示一个占位符?-->
    	<!-- #{id}:里面的id表示输入参数的参数名称,如果该参数为简单类型,那么#{}里面的参数可以任意 -->
    	<!-- parameterType:输入参数的java类型 -->
    	<!-- resultType:输出结果的所映射的java类型(单条结果所对应的java类型) -->
    	<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
    		SELECT * FROM USER WHERE id = #{id}
    	</select>
    </mapper>


    在全局配置文件SqlMapperConfig中添加该映射文件

    测试代码:

    public class UserMapperTest {
    
    	private SqlSessionFactory sqlSessionFactory ;
    	
    	@Before
    	public void setUp() throws IOException {
    		InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    	}
    	@Test
    	public void testFindUserById() {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		//获取UserMapper的代理类
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		User user = userMapper.findUserById(10);
    		System.out.println(user);
    		sqlSession.close();
    	}
    
    }

    八、Mybatis一级缓存

    mybatis提供查询缓存,如果缓存中有数据,就不用从数据库中获取,用于减轻数据压力,提高系统性能


    一级缓存是sqlSession级别的缓存,在操作数据库的时候,需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的SqlSession的缓存区域(HashMap)是互相不受影响的。


    mybatis默认是支持一级缓存的。

    8.1.证明一级缓存的存在:

    验证方法:

    public void testOneLevelCache() {
    		SqlSession sqlSession = sqlSessionFactory.openSession();
    		//获取UserMapper的代理类
    		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    		//第一次查询
    		User user = userMapper.findUserById(10);
    		System.out.println(user);
    		//第二次查询
    		User user2 = userMapper.findUserById(10);
    		System.out.println(user2);
    		
    		sqlSes

    通过执行该方法,查看打印日志可以看出就执行了一次sql语句的查询,因此可以判断第二次查询是没有从数据库中进行查询的。


    那当什么时候一级缓存会被清空呢?

    通过测试发现,当在第二次查询数据库之前对数据库进行了写的操作后,第二次查询就不会从缓存中查询结果,而是直接从数据中查询结果。另一种就是在第一查询结果后,手动的方式清空一级缓存(使用sqlSession.clearCache();方法。)

    测试方法:


    九、Mybatis二级缓存

    二级缓存是Mapper级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是夸SqlSession的。(二级缓存Mybatis默认是关闭的,需要自己去手动配置开启或可以自己选择用哪个厂家的缓存来作为二级缓存)

    9.1.开启二级缓存

    首先在全局配置文件中配置开启二级缓存功能:

    <!-- 开启二级缓存总开关 -->
    <setting name="cacheEnabled" value="true"/>

    在映射文件中去选择是否该映射文件使用二级缓存:

    如果使用就进行以下配置,如果不是用,就不需要对映射文件做任何修改。

    <!-- 开启本mapper下的namespace的二级缓存,默认使用的是mybatis提供的PerpetualCache -->
    <cache></cache> 

    也可以设置选择二级缓存提工商

    以下是我选择使用EhcacheCache缓存(注意:在使用EhcacheCache缓存需要导入jar包)



    最后就是,在需要缓存的po类中去实现序列化:

    验证二级缓存是否开启:

    验证方法如下:

    @Test
    	//测试二级缓存是否开启
    	public void testTwoLevelCache() {
    		SqlSession sqlSession1 = sqlSessionFactory.openSession();
    		SqlSession sqlSession2 = sqlSessionFactory.openSession();
    		SqlSession sqlSession3 = sqlSessionFactory.openSession();
    		//获取UserMapper的代理类
    		UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    		UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
    		UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
    		
    		User user1 = userMapper1.findUserById(10);
    		System.out.println(user1);
    		//当sqlSession1执行close()的时候,才把sqlSession1查询的结果写到二级缓存中去。
    		sqlSession1.close();
    		
    //		User u = new User();
    //		u.setUsername("小胖");
    //		u.setAddress("成都");
    //		u.setSex("2");
    //		u.setBirthday(new Date());
    //		userMapper3.insertUser(u);
    //		//当其中任何一个sqlSession执行commit()方法后将刷新整个缓存区
    //		sqlSession3.commit();
    //		sqlSession3.close();
    		
    		//第二次查询
    		User user2 = userMapper2.findUserById(10);
    		System.out.println(user2);
    		sqlSession2.close();
    		
    		
    	}
    

    测试结果:


    同时通过测试,当在第二次查询的时候,向数据库提交数据后,就会对缓存进行刷新,这样在第二次查询的时候就没有走缓存,而是走的数据库。

    9.2.禁用二级缓存

    由于二级缓存默认是关闭的,如果不开启就不会使用二级缓存。如果,我们开启了二级缓存,而在有些查询结果集中,不需要受到二级缓存影响,该怎么去做呢?

    在select查询中,默认是使用了二级缓存,如果不想使用二级缓存,就在select标签中有一个useCache的属性设置为false,就代表不使用二级缓存,每次进行查询数据都不会从缓存总获取,而是直接从数据库中进行查询。useCache的默认值是true,即代表statement使用二级缓存。


    9.3.刷新二级缓存

    在statement中设置flushCache=true,可以刷新二级缓存。默认情况下,select语句中的flushCache是false。如果是insert、update、delete语句,那么flushCache的默认值是true。如果将select语句中的flushCache值改为true,就意味着查询语句的二级缓存失效,每次查询都会从数据库进行查询。如果将select语句的flushCache值为false,就代表该查询语句使用了二级缓存,如果在数据库中修改了数据,而二级缓存中的数据还是原来的数据,那么这样就会出现脏读。

    flushCache设置如下:



    展开全文
  • MyBatis 快速入门和重点详解

    万次阅读 多人点赞 2018-03-27 15:22:27
    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和...

    1.定义

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

    官网

    http://www.mybatis.org/

    2.使用 MyBatis

    1. 编程式
      即不与其他框架集成使用mybatis。
      入门实例参考:
      http://www.mybatis.org/mybatis-3/zh/getting-started.html
      我自己的实例:https://github.com/ljmomo/learn-mybatis mybatis-demo 模块

    2. 集成式 managed 集成到 spring
      集成Spring http://www.mybatis.org/spring/zh/

      工作中使用一般是 集成式 managed 集成到spring 并且使用
      MyBatis Generator 生成生成 Bean 和 Mapper。在IDEA中如何使用可以参考我的另一篇文章: IDEA中使用MyBatis Generator

    3.作用域(Scope)和生命周期

    类名称 SCOPE
    SqlSessionFactoryBuilder method
    SqlSessionFactory application
    SqlSession request/method (可以认为是线程级)
    Mapper method

    详细说明:
    http://www.mybatis.org/mybatis-3/zh/getting-started.html

    4.mybatis config文件

    1. typeAliases 类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:
    <typeAliases>
      <typeAlias alias="Author" type="domain.blog.Author"/>
      <typeAlias alias="Blog" type="domain.blog.Blog"/>
      <typeAlias alias="Comment" type="domain.blog.Comment"/>
      <typeAlias alias="Post" type="domain.blog.Post"/>
      <typeAlias alias="Section" type="domain.blog.Section"/>
      <typeAlias alias="Tag" type="domain.blog.Tag"/>
    </typeAliases>

    2.typeHandlers
    无论是 MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
    你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。比如:

    @MappedJdbcTypes(JdbcType.VARCHAR)
    public class JunliTypeHandler extends BaseTypeHandler<String> {
        @Override
        public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
            preparedStatement.setString(i, s + "LIJUN");
        }
    
        @Override
        public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
            return resultSet.getString(s)+"LIJUN";
        }
    
        @Override
        public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
            return resultSet.getString(i);
        }
    
        @Override
        public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
            return callableStatement.getString(i);
        }
    }
    
    
    <!-- mybatis-config.xml -->
       <typeHandlers>
            <typeHandler handler="com.junli.mybatis.demo.mybatis.JunliTypeHandler"/>
        </typeHandlers>

    @MappedJdbcTypes(JdbcType.VARCHAR) 使用这个的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。preparedStatement.setString(i, s + “LIJUN”); 表示在所有String类型后面加上 LIJUN 但是有时候我们只是想特定的字段加上LIJUN。可以如下配置(mybatis-config.xml 就不需要了):

    //插入
     insert into test (id, nums, name
        )
        values (#{id,jdbcType=INTEGER}, #{nums,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR,typeHandler=com.junli.mybatis.demo.mybatis.JunliTypeHandler}
        )
    
    
    
     //返回   
    <resultMap id="BaseResultMap" type="com.junli.mybatis.beans.Test">
        <id column="id" jdbcType="INTEGER" property="id" />
        <result column="nums" jdbcType="INTEGER" property="nums" />
        <result column="name" jdbcType="VARCHAR" property="name" typeHandler="com.junli.mybatis.demo.mybatis.JunliTypeHandler"/>
      </resultMap>    

    3.插件(plugins)
    MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
    - Executor (update, query, flushStatements, commit, rollback,getTransaction, close, isClosed)
    - ParameterHandler (getParameterObject, setParameters)
    - ResultSetHandler (handleResultSets, handleOutputParameters)
    - StatementHandler (prepare, parameterize, batch, update, query)

    下面通过自定义插件来打印出查询的sql语句:

    @Intercepts({@Signature(type = Executor.class,
            method = "query",
            args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
    public class JunliPlugin  implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
            System.out.println(String.format("plugin output sql = %s , param=%s", boundSql.getSql(),boundSql.getParameterObject()));
            return invocation.proceed();
        }
        @Override
        public Object plugin(Object o) {
            return Plugin.wrap(o,this);
        }
        @Override
        public void setProperties(Properties properties) {
    
        }

    配置插件:

      <plugins>
          <plugin interceptor="com.junli.mybatis.demo.mybatis.JunliPlugin"/>
      </plugins>

    4.映射器(mappers)
    既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。 Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:

     <mappers>
        <mapper resource="xml/TestMapper.xml"/>
        <mapper resource="xml/PostsMapper.xml"/>
    </mappers>

    详细的mybatis config 参考官网: http://www.mybatis.org/mybatis-3/zh/configuration.html

    5.Mapper XML 文件 解读

    MyBatis 的真正强大在于它的映射语句,也是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。
    SQL映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

    • cache – 给定命名空间的缓存配置。
    • cache-ref – 其他命名空间缓存配置的引用。
    • resultMap
    • – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
    • sql – 可被其他语句引用的可重用语句块。
    • insert – 映射插入语句
    • update – 映射更新语句
    • delete – 映射删除语句
    • select – 映射查询语句

    主要说一下resultMap
    resultMap 元素有很多子元素和一个值得讨论的结构。

    resultMap

    • constructor - 用于在实例化类时,注入结果到构造方法中
      idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
      arg - 将被注入到构造方法的一个普通结果
    • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
    • result – 注入到字段或 JavaBean 属性的普通结果
    • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个
    • collection – 一个复杂类型的集合嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
    • discriminator – 使用结果值来决定使用哪个 resultMap.

      resultMap 关联查询.
      关联查询分为两种:

    • 关联的嵌套查询

    例子

     <resultMap id="BaseResultMap" type="com.junli.mybatis.beans.Blog">
            <result column="bid" jdbcType="INTEGER" property="bid"/>
            <result column="name" jdbcType="VARCHAR" property="name"/>
            <result column="author_id" jdbcType="INTEGER" property="authorId"/>
            <association property="author" column="author_id" javaType="com.junli.mybatis.beans.Author" select="selectAuthor" />
        </resultMap>
    
        <resultMap id="BaseResultMap_Author" type="com.junli.mybatis.beans.Author">
            <result column="aid" jdbcType="INTEGER" property="aid"/>
            <result column="author_name" jdbcType="VARCHAR" property="authorName"/>
        </resultMap>
    
    
     <select id="selectAuthor" resultType="com.junli.mybatis.beans.Author" resultMap="BaseResultMap_Author">
            SELECT * FROM AUTHOR WHERE aid = #{author_id}
        </select>
    
        <select id="selectById" resultMap="BaseResultMap">
            SELECT  * FROM  blog WHERE bid = #{id} ;
        </select>
    

    我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“BaseResultMap_Author”语句应该被用来加载它的 author 属性。
    其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
    这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:
    1. 你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。
    2. 对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。
    这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。
    MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。所以还有另外一种方法。

    • 关联的嵌套结果
      重新上面的例子
       <resultMap id="blogResult" type="com.junli.mybatis.beans.Blog">
            <result column="bid" jdbcType="INTEGER" property="bid"/>
            <result column="name" jdbcType="VARCHAR" property="name"/>
            <result column="author_id" jdbcType="INTEGER" property="authorId"/>
            <association property="author" column="author_id" javaType="com.junli.mybatis.beans.Author" resultMap="BaseResultMap_Author2"/>
        </resultMap>
    
        <resultMap id="BaseResultMap_Author2" type="com.junli.mybatis.beans.Author">
            <result column="aid" jdbcType="INTEGER" property="aid"/>
            <result column="author_name" jdbcType="VARCHAR" property="authorName"/>
        </resultMap>
    
        <select id="selectBlogById" resultMap="blogResult">
          SELECT
                B.bid,
                B.`name`,
                B.author_id,
                A.aid,
                A.author_name
            FROM
                Blog B
            LEFT OUTER JOIN Author A ON B.author_id = A.aid
            WHERE
                B.bid = #{id}
        </select>
    

    集合的嵌套查询
    我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章。在博客类中, 这可以由下面这样的写法来表示:

    private List<Posts> posts;

    实例:

      <resultMap id="blogResultAndPosts" type="com.junli.mybatis.beans.Blog">
            <collection property="posts" javaType="ArrayList" column="bid"
                        ofType="com.junli.mybatis.beans.Posts" select="selectPostsForBlog"/>
        </resultMap>
    
        <resultMap id="PostsForBlogResult" type="com.junli.mybatis.beans.Posts">
            <result column="pid" jdbcType="INTEGER" property="pid" />
            <result column="post_name" jdbcType="VARCHAR" property="postName" />
            <result column="blog_id" jdbcType="INTEGER" property="blogId" />
        </resultMap>
    
        <select id="selectBlogAndPosts" resultMap="blogResultAndPosts">
            SELECT * FROM BLOG WHERE BID = #{id}
        </select>
    
        <select id="selectPostsForBlog" resultMap="PostsForBlogResult">
            SELECT * FROM POSTS WHERE BLOG_ID = #{bid}
        </select>

    集合的嵌套结果实现:

    <resultMap id="blogResultAndPostsResultQuery" type="com.junli.mybatis.beans.Blog">
            <result column="bid" jdbcType="INTEGER" property="bid"/>
            <result column="name" jdbcType="VARCHAR" property="name"/>
            <result column="author_id" jdbcType="INTEGER" property="authorId"/>
            <collection property="posts" javaType="ArrayList" column="bid" ofType="com.junli.mybatis.beans.Posts">
                <result column="pid" jdbcType="INTEGER" property="pid" />
                <result column="post_name" jdbcType="VARCHAR" property="postName" />
                <result column="blog_id" jdbcType="INTEGER" property="blogId" />
            </collection>
        </resultMap>
    
        <select id="selectBlogAndPostsResultQuery" resultMap="blogResultAndPostsResultQuery">
            SELECT
                B.bid,
                B.`name`,
                B.author_id,
                p.pid,
                p.post_name,
                p.blog_id
            FROM
                Blog B
            LEFT  JOIN posts p ON p.blog_id = b.bid
            WHERE
                B.bid = #{id}
        </select>

    缓存:
    Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指SqlSession级别的缓存,当在同一个SqlSession中进行相同的SQL语句查询时,第二次以后的查询不会从数据库查询,而是直接从缓存中获取,一级缓存最多缓存1024条SQL。二级缓存是指可以跨SqlSession的缓存。
    Mybatis中进行SQL查询是通过org.apache.ibatis.executor.Executor接口进行的,总体来讲,它一共有两类实现,一类是BaseExecutor,一类是CachingExecutor。前者是非启用二级缓存时使用的,而后者是采用的装饰器模式,在启用了二级缓存时使用,当二级缓存没有命中时,底层还是通过BaseExecutor来实现的。

    • 一级缓存
      一级缓存是默认启用的,在BaseExecutor的query()方法中实现,底层默认使用的是PerpetualCache实现,PerpetualCache采用HashMap存储数据。一级缓存会在进行增、删、改操作时进行清除。
    • 二级缓存
      二级缓存是默认启用的,如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。
      <settings>
    
          <setting name="cacheEnabled" value="false" />
    
       </settings>

    我们要想使用二级缓存,是需要在对应的Mapper.xml文件中定义其中的查询语句需要使用哪个cache来缓存数据的。这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。但是需要注意的是对于同一个Mapper来讲,它只能使用一个Cache,当同时使用了和时使用定义的优先级更高。Mapper使用的Cache是与我们的Mapper对应的namespace绑定的,一个namespace最多只会有一个Cache与其绑定。

    • 自定义cache
      前面提到Mybatis的Cache默认会使用PerpetualCache存储数据,如果我们不想按照它的逻辑实现,或者我们想使用其它缓存框架来实现,比如使用Ehcache、Redis等,这个时候我们就可以使用自己的Cache实现,Mybatis是给我们留有对应的接口,允许我们进行自定义的。要想实现自定义的Cache我们必须定义一个自己的类来实现Mybatis提供的Cache接口,实现对应的接口方法。
    **
     * 自定义缓存
     */
    public class JunliCache implements Cache {
        private ReadWriteLock lock = new ReentrantReadWriteLock();
        private ConcurrentHashMap<Object, Object> cache = new ConcurrentHashMap<Object, Object>();
        private String id;
    
        public JunliCache() {
            System.out.println("初始化-1!");
        }
    
        /**
         * 必须有该构造函数
         */
        public JunliCache(String id) {
            System.out.println("初始化-2!");
            this.id = id;
        }
    
        /**
         * 获取缓存编号
         */
        @Override
        public String getId() {
            System.out.println("得到ID:" + id);
            return id;
        }
    
    
        /***
         * 获取缓存对象的大小
         * @return int
         */
        @Override
        public int getSize() {
            System.out.println("获取缓存大小!");
            return 0;
        }
    
        /**
         * 保存key值缓存对象
         *
         * @param key   key
         * @param value value
         */
        @Override
        public void putObject(Object key, Object value) {
            System.out.println("往缓存中添加元素:key=" + key + ",value=" + value);
            cache.put(key, value);
        }
    
    
        /**
         * 通过KEY
         *
         * @param key key
         * @return Object
         */
        @Override
        public Object getObject(Object key) {
            System.out.println("通过kEY获取值:" + key);
            System.out.println("OVER");
            System.out.println("=======================================================");
            System.out.println("值为:" + cache.get(key));
            System.out.println("=====================OVER==============================");
            return cache.get(key);
        }
    
    
        /**
         * 通过key删除缓存对象
         *
         * @param key key
         * @return
         */
        @Override
        public Object removeObject(Object key) {
            System.out.println("移除缓存对象:" + key);
            return null;
        }
    
    
        /**
         * 清空缓存
         */
        @Override
        public void clear() {
            System.out.println("清除缓存!");
            cache.clear();
        }
    
    
        /**
         * 获取缓存的读写锁
         *
         * @return ReadWriteLock
         */
        @Override
        public ReadWriteLock getReadWriteLock() {
            System.out.println("获取锁对象!!!");
            return lock;
        }
    }

    更多Mapper XML 文件配置参考 http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html

    5.动态 SQL

    MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似.MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

    • if
      <if test="title != null">
        AND title like #{title}
      </if>
    • choose (when, otherwise)
     <choose>
        <when test="title != null">
          AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
          AND author_name like #{author.name}
        </when>
        <otherwise>
          AND featured = 1
        </otherwise>
      </choose>
    • trim (where, set)
      <where> 
        <if test="state != null">
             state = #{state}
        </if> 
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author.name != null">
            AND author_name like #{author.name}
        </if>
      </where>
    
      <trim prefix="WHERE" prefixOverrides="AND |OR ">
      ... 
     </trim>
    
      <set>
          <if test="username != null">username=#{username},</if>
          <if test="password != null">password=#{password},</if>
          <if test="email != null">email=#{email},</if>
          <if test="bio != null">bio=#{bio}</if>
        </set>
    
    • foreach
     <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>

    更多动态SQL参考:http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

    文章稍微有点长,本文中涉及的源码参考我的源码mybatis-demo模块。

    源码地址

    展开全文
  • MyBatis面试专题以及答案

    千次阅读 多人点赞 2020-06-11 16:02:03
    1、什么是 MyBatis? 答:MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。 2、讲下 MyBatis 的缓存 答:MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,默认就有,二级缓 存放在...

    1、什么是 MyBatis?
    答:MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。
    2、讲下 MyBatis 的缓存
    答:MyBatis 的缓存分为一级缓存和二级缓存,一级缓存放在 session 里面,默认就有,二级缓
    存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现 Serializable 序列化
    接口(可用来保存对象的状态),可在它的映射文件中配置
    3、Mybatis 是如何进行分页的?分页插件的原理是什么?
    答:
    1)Mybatis 使用 RowBounds 对象进行分页,也可以直接编写 sql 实现分页,也可以使用
    Mybatis 的分页插件。
    2)分页插件的原理:实现 Mybatis 提供的接口,实现自定义插件,在插件的拦截方法内拦
    截待执行的 sql,然后重写 sql。
    举例:select * from student,拦截 sql 后重写为:select t.* from (select * from student)t
    limit 0,10
    4、简述 Mybatis 的插件运行原理,以及如何编写一个插件?
    答:
    1)Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、
    Executor 这 4 种接口的插件,Mybatis 通过动态代理,为需要拦截的接口生成代理对象以实
    现接口方法拦截功能,每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是
    InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
    2)实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定
    要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
    5、Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?
    答:
    1)Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑
    判断和动态拼接 sql 的功能。
    2)Mybatis 提供了 9 种动态 sql 标签:
    trim|where|set|foreach|if|choose|when|otherwise|bind。 3)其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼
    接 sql,以此来完成动态 sql 的功能。
    6、#{}和KaTeX parse error: Expected 'EOF', got '#' at position 16: {}的区别是什么? 答: 1)#̲{}是预编译处理,{}是字符串替换。
    2)Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法
    来赋值;
    3)Mybatis 在处理{}时,就是把{}替换成变量的值。
    4)使用#{}可以有效的防止 SQL 注入,提高系统安全性。
    7、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别在哪里?
    答:Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象
    时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或
    关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。
    8、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?
    答:
    1)Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association
    指的就是一对一,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否
    启用延迟加载 lazyLoadingEnabled=true|false。 2)它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方
    法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是 null 值,那么就会单
    独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的
    对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原
    理。
    9、MyBatis 与 Hibernate 有哪些不同?
    答:
    1)Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己
    编写 Sql 语句,不过 mybatis 可以通过 XML 或注解方式灵活配置要运行的 sql 语句,并将
    java 对象和 sql 语句映射生成最终执行的 sql,最后将 sql 执行的结果再映射生成 java 对
    象。
    2)Mybatis 学习门槛低,简单易学,程序员直接编写原生态 sql,可严格控制 sql 执行性
    能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如互联网软件、企业运
    营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。但是灵活的
    前提是 mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件则需要自定
    义多套 sql 映射文件,工作量大。
    3)Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件(例如
    需求固定的定制化软件)如果用 hibernate 开发可以节省很多代码,提高效率。但是
    Hibernate 的缺点是学习门槛高,要精通门槛更高,而且怎么设计 O/R 映射,在性能和对象
    模型之间如何权衡,以及怎样用好 Hibernate 需要具有很强的经验和能力才行。
    总之,按照用户的需求在有限的资源环境下只要能做出维护性、扩展性良好的软件架构都
    是好架构,所以框架只有适合才是最好。
    10、MyBatis 的好处是什么?
    答:
    1)MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的
    维护带来了很大便利。
    2)MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象,
    大大简化了 Java 数据库编程的重复工作。
    3)因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活
    控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复
    杂查询。
    11、简述 Mybatis 的 Xml 映射文件和 Mybatis 内部数据结构之间的映射关系?
    答:Mybatis 将所有 Xml 配置信息都封装到 All-In-One 重量级对象 Configuration 内部。在
    Xml 映射文件中,标签会被解析为 ParameterMap 对象,其每个子元素会
    被解析为 ParameterMapping 对象。标签会被解析为 ResultMap 对象,其每个子
    元素会被解析为 ResultMapping 对象。每一个、、、标签
    均会被解析为 MappedStatement 对象,标签内的 sql 会被解析为 BoundSql 对象。
    12、什么是 MyBatis 的接口绑定,有什么好处?
    答:接口映射就是在 MyBatis 中任意定义接口,然后把接口里面的方法和 SQL 语句绑定,我们
    直接调用接口方法就可以,这样比起原来了 SqlSession 提供的方法我们可以有更加灵活的选
    择和设置.
    13、接口绑定有几种实现方式,分别是怎么实现的?
    答:接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上
    @Select@Update 等注解里面包含 Sql 语句来绑定,另外一种就是通过 xml 里面写 SQL 来绑
    定,在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名.
    14、什么情况下用注解绑定,什么情况下用 xml 绑定?
    答:当 Sql 语句比较简单时候,用注解绑定;当 SQL 语句比较复杂时候,用 xml 绑定,一般用
    xml 绑定的比较多
    15、MyBatis 实现一对一有几种方式?具体怎么操作的?
    答:有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次,通过在 resultMap 里面
    配置 association 节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面
    的结果的外键 id,去再另外一个表里面查询数据,也是通过 association 配置,但另外一个表的
    查询通过 select 属性配置。
    16、Mybatis 能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区
    别?
    答:能,Mybatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的
    关联查询,多对一查询,其实就是一对一查询,只需要把 selectOne()修改为 selectList()即
    可;多对多查询,其实就是一对多查询,只需要把 selectOne()修改为 selectList()即可。
    关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对
    象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分
    列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询,
    就可以把主对象和其关联对象查出来。
    17、MyBatis 里面的动态 Sql 是怎么设定的?用什么语法?
    答:MyBatis 里面的动态 Sql 一般是通过 if 节点来实现,通过 OGNL 语法来实现,但是如果要
    写的完整,必须配合 where,trim 节点,where 节点是判断包含节点有内容就插入 where,否则不
    插入,trim 节点是用来判断如果动态语句是以 and 或 or 开始,那么会自动把这个 and 或者 or
    取掉。
    18、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有哪些映射形式?
    答:
    第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。
    第二种是使用 sql 列的别名功能,将列别名书写为对象属性名,比如 T_NAME AS NAME,对
    象属性名一般是 name,小写,但是列名不区分大小写,Mybatis 会忽略列名大小写,智能
    找到与之对应对象属性名,你甚至可以写成 T_NAME AS NaMe,Mybatis 一样可以正常工
    作。
    有了列名与属性名的映射关系后,Mybatis 通过反射创建对象,同时使用反射给对象的属性
    逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。
    19、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之外,还有哪些标签?
    答:还有很多其他的标签,、、、、
    ,加上动态 sql 的 9 个标签,
    trim|where|set|foreach|if|choose|when|otherwise|bind 等,其中为 sql 片段标签,通
    过标签引入 sql 片段,为不支持自增的主键生成策略标签。
    20、当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定 pojo?
    答:
    1)通过在查询的 sql 语句中定义字段名的别名。
    2)通过来映射字段名和实体类属性名的一一对应的关系。
    21、模糊查询 like 语句该怎么写
    答:
    1)在 java 中拼接通配符,通过#{}赋值
    2)在 Sql 语句中拼接通配符 (不安全 会引起 Sql 注入)
    22、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应, Dao 的工作原理,是否可以重
    载?
    答:不能重载,因为通过 Dao 寻找 Xml 对应的 sql 的时候全限名+方法名的保存和寻找策
    略。接口工作原理为 jdk 动态代理原理,运行时会为 dao 生成 proxy,代理对象会拦截接口
    方法,去执行对应的 sql 返回数据。
    23、Mybatis 映射文件中,如果 A 标签通过 include 引用了 B 标签的内容,请问,B 标签能
    否定义在 A 标签的后面,还是说必须定义在 A 标签的前面?
    答:虽然 Mybatis 解析 Xml 映射文件是按照顺序解析的,但是,被引用的 B 标签依然可以
    定义在任何地方,Mybatis 都可以正确识别。原理是,Mybatis 解析 A 标签,发现 A 标签引
    用了 B 标签,但是 B 标签尚未解析到,尚不存在,此时,Mybatis 会将 A 标签标记为未解
    析状态,然后继续解析余下的标签,包含 B 标签,待所有标签解析完毕,Mybatis 会重新
    解析那些被标记为未解析的标签,此时再解析 A 标签时,B 标签已经存在,A 标签也就可
    以正常解析完成了。
    24、Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?
    答:不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复;如果没有配置
    namespace,那么 id 不能重复;毕竟 namespace 不是必须的,只是最佳实践而已。原因就
    是 namespace+id 是作为 Map<String, MappedStatement>的 key 使用的,如果没有
    namespace,就剩下 id,那么,id 重复会导致数据互相覆盖。有了 namespace,自然 id 就
    可以重复,namespace 不同,namespace+id 自然也就不同。
    25、Mybatis 中如何执行批处理?
    答:使用 BatchExecutor 完成批处理。
    26、Mybatis 都有哪些 Executor 执行器?它们之间的区别是什么?
    答:Mybatis 有三种基本的 Executor 执行器,SimpleExecutor、ReuseExecutor、
    BatchExecutor。1)SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对
    象,用完立刻关闭 Statement 对象。2)ReuseExecutor:执行 update 或 select,以 sql 作为
    key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,
    而是放置于 Map3)BatchExecutor:完成批处理。
    27、Mybatis 中如何指定使用哪一种 Executor 执行器?
    答:在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给
    DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。
    28、Mybatis 执行批量插入,能返回数据库主键列表吗?
    答:能,JDBC 都能,Mybatis 当然也能。
    29、Mybatis 是否可以映射 Enum 枚举类?
    答:Mybatis 可以映射枚举类,不单可以映射枚举类,Mybatis 可以映射任何对象到表的一
    列上。映射方式为自定义一个 TypeHandler,实现 TypeHandler 的 setParameter()和
    getResult()接口方法。TypeHandler 有两个作用,一是完成从 javaType 至 jdbcType 的转换,
    二是完成 jdbcType 至 javaType 的转换,体现为 setParameter()和 getResult()两个方法,分别
    代表设置 sql 问号占位符参数和获取列查询结果。
    30、如何获取自动生成的(主)键值?
    答:配置文件设置 usegeneratedkeys 为 true
    31、在 mapper 中如何传递多个参数?
    答:
    1)直接在方法中传递参数,xml 文件用#{0} #{1}来获取
    2)使用 @param 注解:这样可以直接在 xml 文件中通过#{name}来获取
    32、resultType resultMap 的区别?
    答:
    1)类的名字和数据库相同时,可以直接设置 resultType 参数为 Pojo 类 2)若不同,需要设置 resultMap 将结果名字和 Pojo 名字进行转换
    33、使用 MyBatis 的 mapper 接口调用时有哪些要求?
    答:
    1)Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同
    2)Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的
    类型相同
    3)Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型
    相同
    4)Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径。
    34、Mybatis 比 IBatis 比较大的几个改进是什么?
    答:
    1)有接口绑定,包括注解绑定 sql 和 xml 绑定 Sql
    2)动态 sql 由原来的节点配置变成 OGNL 表达式 3) 在一对一,一对多的时候引进了
    association,在一对多的时候引入了 collection 节点,不过都是在 resultMap 里面配置
    35、IBatis 和 MyBatis 在核心处理类分别叫什么?
    答:IBatis 里面的核心处理类交 SqlMapClient,MyBatis 里面的核心处理类叫做 SqlSession。
    36、IBatis 和 MyBatis 在细节上的不同有哪些?
    答:
    1)在 sql 里面变量命名有原来的#变量# 变成了#{变量} 2)原来的变量变成了${变量} 3)原来在 sql 节点里面的 class 都换名字交 type
    4)原来的 queryForObject queryForList 变成了 selectOne selectList5)原来的别名设置在映
    射文件里面放在了核心配置文件里

    展开全文
  • Mybatis 入门(一)

    千次阅读 2020-06-21 18:38:15
    Mybatis入门 A Simple Demo 1. 首先我们在数据库中建一个 mydemo 库,在其下建一个表,建表示例 SQL 如下: DROP TABLE IF EXISTS `country`; CREATE TABLE `country` ( `id` int(11) NOT NULL AUTO_INCREMENT ...
  • DROP TABLE IF EXISTS `bas_dictionary`; CREATE TABLE `bas_dictionary` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL COMMENT '字典名称', `key` varchar(50) NOT NULL ...
  • 解决mybatis在mysql中查询字段为关键字的问题 今天做项目的时候,调用mybatis进行数据查询,但是一直报错,查了半天,终于知道是在创建mysql数据库表时将function作为字段名,所以在查询的时候MySQL无法进行正常...
  • mybatis中字段名与关键字相同

    千次阅读 2019-01-23 10:01:51
    在字段上添加反双引号,就是~键; 如图: 
  • 解决办法 在实体类字段上加上这个注解就可以@TableField("group"),注意:字段名要加反引号,就是Esc按键下面那个 @TableField("`group`") private Integer group;
  • 超详细MyBatis入门讲解

    万次阅读 多人点赞 2018-05-22 20:13:20
    1.Mybatis简介 1.1基本内容 1.2JDBC的问题 1.3Mybatis架构 2.Mybatis框架搭建 2.1入门程序 2.2小结 2.2和Hibernate的区别 3.Dao开发方法 4.Mapper动态代理开发 5.配置文件说明 5.1配置内容 5.2properties(属性) ...
  • MyBatis

    千次阅读 2018-09-11 08:46:38
    之前介绍了一些比较适用的框架,今天来介绍一个持久层框架——MyBatisMybatis 介绍: 框架的作用: Mybatis是一个数据持久层框架(dao层),数据持久化,将数据从内存中存储到硬盘中 框架的特点: Jdbc的...
  • MyBatis面试题(2020最新版)

    万次阅读 多人点赞 2020-05-06 14:14:16
    整理好的MyBatis面试题库,史上最全的MyBatis面试题,MyBatis面试宝典,特此分享给大家 MyBatis 介绍 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC ...
  • Mybatis-Plus和Mybatis的区别

    万次阅读 多人点赞 2020-10-19 17:42:56
    Mybatis-Plus是一个Mybatis的增强工具,它在Mybatis的基础上做了增强,却不做改变。我们在使用Mybatis-Plus之后既可以使用Mybatis-Plus的特有功能,又能够正常使用Mybatis的原生功能。Mybatis-Plus(以下简称MP)是为...
  • mybatis框架需要的所有jar包

    万次阅读 2018-08-12 17:58:43
    https://github.com/mybatis/mybatis-3/releases?after=mybatis-3.2.8
  • mybatis的jar包下载地址

    万次阅读 2018-03-19 16:34:45
    mybatis的jar包下载地址:https://github.com/mybatis/mybatis-3/releasesmybatis和Spring整合jar包下载地址:https://github.com/mybatis/spring/releases不过今天不知道出了什么问题在上面的网址中下载不了jar包,...
  • spring集成mybatis版本对照表

    万次阅读 2019-03-14 18:10:44
    MyBatis-Spring需要Java 5或更高版本以及各MyBatis和Spring版本: MyBatis-Spring MyBatis Spring 1.0.0 and 1.0.1 3.0.1 to 3.0.5 3.0.0 or higher 1.0.2 3.0.6 3.0.0 or higher 1.1.0 or...
  • tkmybatis VS mybatisplus

    千次阅读 2019-07-30 09:52:59
    https://blog.csdn.net/u013076044/article/details/95376200#4__33
  • mybatis判断是否为空或null

    万次阅读 2013-08-07 14:59:29
    AND type = #{type}
  • MybatisX idea 快速开发插件

    万次阅读 2018-11-05 18:25:44
    MybatisX 辅助 idea 快速开发插件,为效率而生  一.官方安装MybatisX,按ctrl+alt+s,弹出Settings,然后在plugins中搜索MybatisX,安装即可   一.Jar安装MybatisX,按ctrl+alt+s,弹出Settings,然后在...
  • idea 插件Free Mybatis plugin

    万次阅读 2018-03-13 10:20:42
    实现功能,点击dao层方法直接跳转到对应xml 插件
1 2 3 4 5 ... 20
收藏数 351,053
精华内容 140,421
关键字:

mybatis