myBatis基础学习

本文记录了mybatis中基础知识的学习,下一篇会介绍mybatis的高级用法,相关代码和参考资料在这里,下面正式开始学习


1.原生态JDBC程序中的问题总结

  • 传统使用JDBC对java文件与数据库进行连接的步骤和代码如下:

  首先在导入jdbc驱动包之后,创建数据库并编写数据库连接代码

package jdbc_test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.mysql.jdbc.Driver;

public class jdbcTest {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // TODO Auto-generated method stub
        // 数据库的连接
        Connection connection = null;
        // 预编译的Statement,使用预编译的Statement提高数据库的性能
        // 通过预编译的Statement向数据库发生sql语句,数据库要对sql语句进行编译,编译完之后数据库会对得到的结果集存到数据库端的缓存中
        // 下一次再发送相同的sql语句,数据库则不用重新编译,直接从缓存中拿来结果集即可,所以提高了数据库的性能。
        PreparedStatement preparedStatement = null;
        // 结果集
        ResultSet resultSet = null;
        try {
            // 加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 通过驱动管理类获取数据库连接
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_learning?useSSL=false",
                    "root", "7458");
            // 定义sql语句,其中?表示占位符
            String sqlString = "select * from user where username = ?";
            // 获取预处理statement
            preparedStatement = connection.prepareStatement(sqlString);
            // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
            preparedStatement.setString(1, "王五");
            // 向数据库发出sql执行查询,查询出结果集
            resultSet = preparedStatement.executeQuery();
            // 遍历查询结果集
            while (resultSet.next()) {
                System.out.println(resultSet.getString("id") + " " + resultSet.getString("username"));
            }
        } finally {
            // TODO: handle finally clause
            // 释放资源(倒着释放)
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
            }
        }
    }

}
View Code
  • 分析问题:
    • 数据库的连接,使用时就创建,不使用时立即释放,对数据库进行频繁的连接开启和关闭,造成数据库资源的浪费,影响数据库的性能;
      • 设想:使用数据库连接池管理数据库的连接。
    • 将sql语句硬编码到java文件中,如果sql语句修改,需要重新编译java代码,不利于系统维护。
      • 设想:将sql语句配置到xml文件中,即使sql变化,不需要对java代码进行重新编译。
    • 向preparedStatement中设置参数,对占位符设置和参数值的设置同样硬编码到java代码中,不利于系统维护。
      • 设想:将sql语句及占位符号和参数全部配置到xml中。
    • 从resultSet中遍历结果集数据时,也存在硬编码,将获取表的字段进行硬编码,不利于系统维护。
      • 设想:将查询的结果集自动映射成java对象。

2.mybatis框架 

  • 什么是mybatis

  mybatis是一个持久层的框架,是apache下的顶级项目,现在被托管到github上,它可以让程序将主要的精力放在sql上,通过mybatis提供的映射方式,自由灵活的生成(半自动化,大部分需要程序员编写sql)满足需要的sql语句,将preparedStatement中的输入参数自动进行输入映射,将查询结果集灵活映射成java对象(输出映射)。

  • mybatis的框架结构

  

3.mybatis的工程结构及入门增删改查的开发步骤

  1. 设置全局配置文件: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>
        <!-- 数据库 -->
        <!-- 和spring整合后, environments配置将废除 -->
        <environments default="development">
            <environment id="development">
                <!-- 使用jdbc事务管理,单独使用时事务控制由mybatis管理 -->
                <transactionManager type="JDBC" />
                <!-- 数据库连接池,单独使用时由mybatis管理 -->
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.jdbc.Driver" />
                    <property name="url"
                        value="jdbc:mysql://localhost:3306/mybatis_learning?useSSL=false" />
                    <property name="username" value="root" />
                    <property name="password" value="7458" />
                </dataSource>
            </environment>
        </environments>
        <!-- 加载映射文件 -->
        <mappers>
            <mapper resource="com/basicOp/mapper/UserMapper.xml" />
        </mappers>
    </configuration>
    View Code
  2. 设置日志文件:log4j.properties
    # Global logging configuration
    # 在开发环境下日志级别是DEBUG,生产环境设置为info或error
    log4j.rootLogger=DEBUG, stdout
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    View Code
  3. 定义自定义pojo对象类型,要与数据库中的数据类型对应
    package com.basicOp.pojo;
    
    import java.util.Date;
    
    public class User {
        private int id;
        private String username;// 用户姓名
        private String sex;// 性别
        private Date birthday;// 生日
        private String address;// 地址
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address="
                    + address + "]";
        }
    
    }
    View Code
  4. 设置mapper文件,准备操作数据库,其命名格式按照UserMapper.xml这种与pojo实体类关联的驼峰格式,这样的好处是代理可以自动识别。最后将映射文件加入到全局配置文件中。
    <?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表示命名空间,每一个命名空间代表着一个pojo的映射器 -->
    <mapper namespace="com.basicOp.mapper.UserMapper">
        <!-- id:标识映射文件中的sql,sql语句封装到mappedStatement对象中,所以将id称为statement的id 
             parameterType:指定输入参数的类型 
             resultType:不管返回是单条还是多条记录,其指定的的单条sql输出结果所映射的java对象类型 #{}:表示占位符 -->
        <select id="findUserById" parameterType="int" resultType="com.basicOp.pojo.User">
            select
            * from user where id = #{id}
        </select>
    
        <select id="findUserByName" parameterType="string"
            resultType="com.basicOp.pojo.User">
            select * from user where username like concat('%',
            #{username}, '%')
        </select>
        
        <!-- parameterType在插入时是对应的pojo对象类型 #{}里面是pojo的属性值,mybatis通过OGNL获取对象的属性值 -->
        <insert id="insertUser" parameterType="com.basicOp.pojo.User">
            insert into user
            (username,birthday,sex,address)
            values
            (#{username},#{birthday},#{sex},#{address})
        </insert>
        
        <delete id="deleteUser" parameterType="java.lang.Integer">
            delete from user where id = #{id}
        </delete>
        
        <update id="updateUser" parameterType="com.basicOp.pojo.User">
            update user set username=#{username},birthday=#{birthday},sex=#{sex},address=#{address} where id=#{id}
        </update>
    </mapper>
    View Code
  5. 创建与mapper所对应的接口文件,命名按照上面的mapper文件一样UserMapper.java,里面包括了操作数据库的方法,方法名与mapper中的id是一样的。
    package com.basicOp.mapper;
    
    import java.util.List;
    
    import com.basicOp.pojo.User;
    
    public interface UserMapper {
        public User findUserById(int id);
        public List<User> findUserByName(String userName);
        public int insertUser(User user);
        public int deleteUser(int id);
        public int updateUser(User user);
    }
    View Code
  6. 按照框架编写程序,包括SqlSessionFactory、SqlSession等,将创建SqlSessionFactory和SqlSession直接放到一个工具类包里,后续在测试类中可直接调用。
    package com.basicOp.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    public class SqlSessionFactoryUtils {
        public static final Class<SqlSessionFactoryUtils> LOCK = SqlSessionFactoryUtils.class;
        private static SqlSessionFactory sqlSessionFactory = null;
    
        private SqlSessionFactoryUtils() {
        }
    
        public static SqlSessionFactory getSqlSessionFactory() {
            synchronized (LOCK) {
                if (sqlSessionFactory != null) {
                    return sqlSessionFactory;
                }
                try {
                    //获得mybatis配置文件
                    String resource = "mybatis-config.xml";
                    //得到配置文件流
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //通过配置文件流创建会话工厂,传入mabatis的配置文件信息
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                } catch (IOException e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                return sqlSessionFactory;
            }
        }
    
        public static SqlSession openSqlSession() {
            if (sqlSessionFactory == null) {
                getSqlSessionFactory();
            }
            //通过工厂得到SqlSession
            return getSqlSessionFactory().openSession();
        }
    }
    View Code
  7. 编写测试类
    package com.basicOp.main;
    
    import java.util.Date;
    import java.util.List;
    
    import org.apache.ibatis.session.SqlSession;
    import org.apache.log4j.Logger;
    
    import com.basicOp.mapper.UserMapper;
    import com.basicOp.pojo.User;
    import com.basicOp.utils.SqlSessionFactoryUtils;
    
    public class basicOpMain {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            // testFindUserById();
            // testFindUserByName();
            // testInsertUser();
            // testDeleteUser();
            testUpdateUser();
    
        }
    
        // 获得通过id查询得到的User结果
        public static void testFindUserById() {
            Logger logger = Logger.getLogger(basicOpMain.class);
            SqlSession sqlSession = null;
            try {
                // 创建会话
                sqlSession = SqlSessionFactoryUtils.openSqlSession();
                // 利用sqlSession获得对应的mapper接口后,开始操作数据库
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = userMapper.findUserById(1);
                logger.info(user.toString());
            } finally {
                // TODO: handle finally clause
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
        }
    
        // 利用username进行模糊查询
        public static void testFindUserByName() {
            Logger logger = Logger.getLogger(basicOpMain.class);
            SqlSession sqlSession = null;
            try {
                // 创建会话
                sqlSession = SqlSessionFactoryUtils.openSqlSession();
                // 利用sqlSession获得对应的mapper接口后,开始操作数据库
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
                List<User> users = userMapper.findUserByName("小明");
                for (User user : users) {
                    logger.info(user.toString());
                }
            } finally {
                // TODO: handle finally clause
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
        }
    
        // 插入数据
        public static void testInsertUser() {
            Logger logger = Logger.getLogger(basicOpMain.class);
            SqlSession sqlSession = null;
            try {
                // 创建会话
                sqlSession = SqlSessionFactoryUtils.openSqlSession();
                // 利用sqlSession获得对应的mapper接口后,开始操作数据库
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = new User();
                user.setUsername("许昕");
                user.setBirthday(new Date());
                user.setSex("1");
                user.setAddress("中国徐州");
                userMapper.insertUser(user);
                // 提交事务
                sqlSession.commit();
                logger.info(user);
            } finally {
                // TODO: handle finally clause
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
        }
    
        // 删除数据
        public static void testDeleteUser() {
            Logger logger = Logger.getLogger(basicOpMain.class);
            SqlSession sqlSession = null;
            try {
                // 创建会话
                sqlSession = SqlSessionFactoryUtils.openSqlSession();
                // 利用sqlSession获得对应的mapper接口后,开始操作数据库
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                userMapper.deleteUser(28);
                // 提交事务
                sqlSession.commit();
    
            } finally {
                // TODO: handle finally clause
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
        }
    
        // 更新数据
        public static void testUpdateUser() {
            Logger logger = Logger.getLogger(basicOpMain.class);
            SqlSession sqlSession = null;
            try {
                // 创建会话
                sqlSession = SqlSessionFactoryUtils.openSqlSession();
                // 利用sqlSession获得对应的mapper接口后,开始操作数据库
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                User user = new User();
                user.setId(27);
                user.setUsername("许昕");
                user.setAddress("江苏徐州");
                userMapper.updateUser(user);
                // 提交事务
                sqlSession.commit();
    
            } finally {
                // TODO: handle finally clause
                if (sqlSession != null) {
                    sqlSession.close();
                }
            }
        }
    
    }
    View Code

4.myBatis开发dao方法(mapper代理的方法)

  • SqlSession使用范围
    • SqlSessionFactoryBuilder

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

    • SqlSessionFactory

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

    • SqlSession

      SqlSession是一个面向用户(程序员)的接口,SqlSession中提供了很多操作数据库的方法:如:selectOne(返回单个对象)、selectList(返回单个或多个对象),SqlSession是线程不安全的,在SqlSesion实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性。所以SqlSession最佳应用场合在方法体内,定义成局部变量使用。

  • 原始dao开发的问题

  在myBatis中若按照原始的dao方法进行开发,首先要定义好mapper映射文件中每个功能的接口,再实现该接口,然后进行测试,这就会出现如下问题:

  1. dao接口实现类方法中存在大量模板方法,设想能否将这些代码提取出来,大大减轻程序员的工作量。
  2. 调用SqlSession方法时将statement的id硬编码了。
  3. 调用SqlSession方法时传入的变量,由于SqlSession方法使用泛型,即使变量类型传入错误,在编译阶段也不报错,不利于程序员开发。

  使用mapper代理的开发,在编写完mapper.xml映射文件后,只需要编写对应mapper接口即可,myBatis可以自动根据接口利用反射实现类的代理对象,mapper接口中的接口名、方法名、参数名、返回值要与mapper文件中对应。

5.全局配置文件mybatis-config.xml中的元素属性

  在全局配置文件中,元素属性的设置是有顺序的,要按照如下顺序进行设置和定义:

  

  • properties属性

  将数据库的连接参数单独的设置在jdbc.properties中,只需要在全局配置文件中加载jdbc.properties的属性值即可,避免了在全局配置文件中的硬编码,方便统一管理。

  注意: MyBatis 将按照下面的顺序来加载属性:

  • 在 properties 元素体内定义的属性首先被读取。
  • 然后会读取properties 元素中resource或 url 加载的属性,它会覆盖已读取的同名属性。
  • 最后读取parameterType传递的属性,它会覆盖已读取的同名属性。

   建议:不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中。在properties文件中定义属性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX

  • setting全局参数配置

  mybatis框架在运行时可以调整一些运行参数,比如:开启二级缓存、开启延迟加载等,会影响全局的运行行为,需要的时候再使用。

  

  

  • typeAliases别名属性

  在mapper.xml中,定义很多的statement,statement需要parameterType指定输入参数的类型、需要resultType指定输出结果的映射类型。如果在指定类型时输入类型全路径,不方便进行开发,可以针对parameterType或resultType指定的类型定义一些别名,在mapper.xml中通过别名定义,方便开发。

  mybatis默认支持别名:

  

  也可以进行自定义别名设置,包括单个别名设置和批量别名设置。

  • typeHandlers类型处理器

  mybatis中通过typeHandlers完成jdbc类型和java类型的转换,通常情况下mybatis提供的类型处理器满足日常需要,不需要自定义。

  

  • objectFactory对象工程和plugins插件一般不用,用的时候查看开发手册即可,environments环境属性即将要被废弃。

  • mapper映射器

  可以通过resource加载单个映射文件;

  可以通过mapper接口加载单个mapper;

  可以批量加载mapper;

6.输入映射

  通过parameterType指定输入参数的类型,类型可以是简单类型、hashmap、pojo包装类型,具体使用方法参考使用手册,pojo包装类型很重要,会比较常用。

7.输出映射

  使用resultType进行输出映射,只有查询出来的列名和pojo中的属性名一致,该列才可以映射成功,如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间做一个映射关系,其他简单的返回的具体使用方法参考使用手册。

8.动态sql

  动态sql可以使得对sql语句的使用更加灵活,通过表达式进行判断,对sql进行灵活拼接、组装,具体使用方法可参考使用手册。

原文地址:https://www.cnblogs.com/yunkaiL/p/11393243.html