mybatis 学习

传统jdbc操作

连接数据库
打开Statement对象
通过Statement执行sql,结果返回ResultSet对象
使用ResultSet对象读取数据转换成POJO对象
关闭数据库相关资源,ResultSet、Statement、Connection、

//创建一个数据库的连接
private static Connection getConnection() {
    Connection connection = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");//加载用户驱动
        String url = "jdbc:mysql://localhost:3306/mybatis";//连接数据库的地址
        String user = "root";//数据库的用户名
        String password = "root";//数据库的密码
        connection = DriverManager.getConnection(url, user, password);//得到一个数据库的连接
    } catch (ClassNotFoundException e) {
        // TODO 自动生成的 catch 块
        System.out.println(JdbcExample.class.getName() + "数据库驱动包未找到!");
        return null;
    } catch (SQLException e) {
        // TODO 自动生成的 catch 块
        System.out.println(JdbcExample.class.getName() + "SQL语句有问题,无法查询成功!");
        return null;
    }
    return connection;//返回该连接
}

/***
 *
 * @param rs 查看结果集是滞关闭
 * @param stmt 预处理SQL是否关闭
 * @param conn 数据库连接是否关闭
 */
private void close(ResultSet rs, Statement stmt, Connection conn) {

    try {
        if (rs != null) {
            rs.close();
        }
    } catch (SQLException e) {
        System.out.println(JdbcExample.class.getName() + "ResultSet 关闭失败!");
    }
    try {
        if (stmt != null) {
            stmt.close();
        }
    } catch (SQLException e) {
        System.out.println(JdbcExample.class.getName() + "Statement 关闭失败!");
    }
    try {
        if (conn != null) {
            conn.close();
        }
    } catch (SQLException e) {
        System.out.println(JdbcExample.class.getName() + "Connection 关闭失败!");
    }
}

/**
 *
 *获取用户
 */
public User getUser(int id) {
    Connection connection = getConnection();//得到该数据库的连接
    PreparedStatement ps = null;//声明一个null的预处理的Statement
    ResultSet rs = null;//声明一个结果集,用来存放SQL的查询后的结果
    try {
        ps = connection.prepareStatement("select * from user where id=?");//对查询的User表的SQL进行预处理编译
        ps.setInt(1, id);//把参数Id设值到数据的条件中
        rs = ps.executeQuery();//执行查询语句。把结果返回到ResultSet结果集中
        while (rs.next()) {//遍历从结果集中取数
            int user_id = rs.getInt("id");//取出Statement的用户id
            String username = rs.getString("username");//取出Statement的用户名
            Date birthday = rs.getDate("birthday");//取出Statement的生日
            String sex = rs.getString("sex");//取出Statement的性别
            String address = rs.getString("address");//取出Statement的用户地址

            User user = new User();//创建一个User类的实体对象POJO
            user.setId(user_id);//存放在user对象中
            user.setUsername(username);
            user.setBirthday(birthday);
            user.setSex(sex);
            user.setAddress(address);

            return user;
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        this.close(rs, ps, connection);
    }
    return null;
}

太多的弊端了,就冗长的代码和硬编码这两项,我们就不能接受。

自定义持久层框架

封装jdbc的代码,实现数据库配置化,sql与代码分离,参数与返回结果自动映射。这就是mybatis的功能。以下是思路:
1.配置文件,sqlMapConfig.xml配置数据库源与相应的mapper文件的路径;xxxMapper.xml文件就是相关的sql文件。
2.配置解析相关,在SqlSessionFactoryBuilder中使用XMLConfigBuilder解析两个配置文件,生成configuration对象,该对象存储datasource与mappedStatement。
3.获取sqlsession,其中封装了相关的jdbc执行代码。根据需求直接调用即可,可选择使用getMapper()方法,代理模式解决硬编码的问题,防止用户输错的调用的方法。

扩展

我们之前先简单完成了查询的操作,现在要将增删改的操作补充上。
在这里插入图片描述
思路:
1.整理jdbc的增删改操作;
2.查看mybatis源码对于增删改的实现,大概理清楚我们需要实现什么;
3.扩展simpleExecutor方法与sqlsession.getMapper方法。

第一步整理jdbc的增删改操作

连接操作

private static Connection getConn() {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/samp_db";
    String username = "root";
    String password = "";
    Connection conn = null;
    try {
        Class.forName(driver); //classLoader,加载对应驱动
        conn = (Connection) DriverManager.getConnection(url, username, password);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

插入操作

private static int insert(Student student) {
    Connection conn = getConn();
    int i = 0;
    String sql = "insert into students (Name,Sex,Age) values(?,?,?)";
    PreparedStatement pstmt;
    try {
        pstmt = (PreparedStatement) conn.prepareStatement(sql);
        pstmt.setString(1, student.getName());
        pstmt.setString(2, student.getSex());
        pstmt.setString(3, student.getAge());
        i = pstmt.executeUpdate();
        pstmt.close();
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}

更新操作

private static int update(Student student) {
    Connection conn = getConn();
    int i = 0;
    String sql = "update students set Age='" + student.getAge() + "' where Name='" + student.getName() + "'";
    PreparedStatement pstmt;
    try {
        pstmt = (PreparedStatement) conn.prepareStatement(sql);
        i = pstmt.executeUpdate();
        System.out.println("resutl: " + i);
        pstmt.close();
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}

删除操作

private static int delete(String name) {
    Connection conn = getConn();
    int i = 0;
    String sql = "delete from students where Name='" + name + "'";
    PreparedStatement pstmt;
    try {
        pstmt = (PreparedStatement) conn.prepareStatement(sql);
        i = pstmt.executeUpdate();
        System.out.println("resutl: " + i);
        pstmt.close();
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return i;
}

第二步查看mybatis源码对于增删改的实现

首先要观察的是sqlsession接口中的增删改方法

public class DefaultSqlSession implements SqlSession {
public interface SqlSession extends Closeable {
	int insert(String statement, Object parameter);

	int update(String statement, Object parameter);

 	int delete(String statement, Object parameter);
 	...
}

接下来是DefaultSqlSession的实现

  @Override
    public int insert(String statement) {
        return insert(statement, null);
    }

    @Override
    public int insert(String statement, Object parameter) {
        return update(statement, parameter);
    }

    @Override
    public int update(String statement) {
        return update(statement, null);
    }

    @Override
    public int update(String statement, Object parameter) {
        try {
            // 标记 dirty ,表示执行过写操作
            dirty = true;
            // 获得 MappedStatement 对象
            MappedStatement ms = configuration.getMappedStatement(statement);
            // 执行更新操作
            return executor.update(ms, wrapCollection(parameter));
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
        } finally {
            ErrorContext.instance().reset();
        }
    }

    @Override
    public int delete(String statement) {
        return update(statement, null);
    }

    @Override
    public int delete(String statement, Object parameter) {
        return update(statement, parameter);
    }

...
}

接下来是SimpleExecutor

public class SimpleExecutor extends BaseExecutor {

    public SimpleExecutor(Configuration configuration, Transaction transaction) {
        super(configuration, transaction);
    }

    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            // 创建 StatementHandler 对象
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            // 初始化 StatementHandler 对象
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 执行 StatementHandler ,进行写操作
            return handler.update(stmt);
        } finally {
            // 关闭 StatementHandler 对象
            closeStatement(stmt);
        }
    }

    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            // 传入参数创建StatementHanlder对象来执行查询
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 创建jdbc中的statement对象
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 执行 StatementHandler  ,进行读操作
            return handler.query(stmt, resultHandler);
        } finally {
            // 关闭 StatementHandler 对象
            closeStatement(stmt);
        }
    }
}

没办法必须进入StatementHandler

public class SimpleStatementHandler extends BaseStatementHandler {

    public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        String sql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        int rows;
        // 如果是 Jdbc3KeyGenerator 类型
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
            // 执行写操作
            statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
            // 获得更新数量
            rows = statement.getUpdateCount();
            // 执行 keyGenerator 的后置处理逻辑
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        // 如果是 SelectKeyGenerator 类型
        } else if (keyGenerator instanceof SelectKeyGenerator) {
            // 执行写操作
            statement.execute(sql);
            // 获得更新数量
            rows = statement.getUpdateCount();
            // 执行 keyGenerator 的后置处理逻辑
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else {
            // 执行写操作
            statement.execute(sql);
            // 获得更新数量
            rows = statement.getUpdateCount();
        }
        return rows;
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        String sql = boundSql.getSql();
        // 执行查询
        statement.execute(sql);
        // 处理返回结果
        return resultSetHandler.handleResultSets(statement);
    }
    }

我们自己之前实现以及刚刚的jdbc增删改操作,都与mybatis使用的不同。所以这里讲一下JDBC中Statement接口提供的execute、executeQuery和executeUpdate之间的区别。
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。

方法executeQuery

用于产生单个结果集的语句,例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句,它几乎是使用最多的 SQL 语句。

方法executeUpdat

用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。

方法execute:

用于执行返回多个结果集、多个更新计数或二者组合的语句。

第三步扩展simpleExecutor方法与sqlsession.getMapper方法

public class simpleExecutor implements  Executor {
   //mybatis中连接并没有直接获取连接
    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        // 1. 注册驱动,获取连接
        Connection connection = configuration.getDataSource().getConnection();

        //2.转换sql语句
        String sql = mappedStatement.getSql();
        //解析后的sql
        BoundSql boundSql = getBoundSql(sql);

        // 3.获取预处理对象:preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4. 设置参数
        //获取到了参数的全路径,类型
        String paramterType = mappedStatement.getParamterType();
        Class<?> paramtertypeClass = getClassType(paramterType);

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            //参数名
            String content = parameterMapping.getContent();

            //反射到对应的属性
            Field declaredField = paramtertypeClass.getDeclaredField(content);
            //暴力访问
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            //setObject 会根据传入参数的类型进行调用相应的set方法
            preparedStatement.setObject(i+1,o);

        }

        int row;
        // 5. 执行sql
        row = preparedStatement.executeUpdate();
        //connect.close
        //preparedStatement.close
        return  row;
    }
}
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }


    //模仿mybatis的实现统一使用update方法进行执行操作,毕竟都是使用excuteUpdate方法
    @Override
    public int insert(String statementid, Object... params)  throws Exception{
        return update( statementid, params) ;
    }

    @Override
    public int delete(String statementid, Object... params)  throws Exception{
        return update( statementid, params) ;
    }
    //模仿query的步骤
    @Override
    public int update(String statementid, Object... params) throws Exception {
         //统一在Executor中执行sql
        simpleExecutor simpleExecutor = new simpleExecutor();
        //获取MappedStatement
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
        //传入数据库配置,sqlStatement,参数
        int row = simpleExecutor.update(configuration, mappedStatement, params);
        return row;
    }
}

修改完毕,我们就实现了增删改查的所有操作

使用getMapper()方法实现增删改查

也就是使用动态代理实现我们的dao层的mapper接口,在动态代理中使用DefaultSqlSession的方法去执行增删改查,代码如下

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    ...

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理来为Dao接口生成代理对象,并返回

        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底层都还是去执行JDBC代码 //根据不同情况,来调用selctList或者selectOne
                // 准备参数 1:statmentid :sql语句的唯一标识:namespace.id= 接口全限定名.方法名
                // 方法名:findAll
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();

                String statementId = className+"."+methodName;

                // 准备参数2:params:args
                // 获取被调用方法的返回值类型
                Type genericReturnType = method.getGenericReturnType();

                //判断是执行select 还是执行增删改
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                if(mappedStatement.getResultType()==null){
                    return update(statementId, args);
                }else{
                    // 判断是否进行了 泛型类型参数化
                    if(genericReturnType instanceof ParameterizedType){
                        List<Object> objects = selectList(statementId, args);
                        return objects;
                    }else{
                        return selectOne(statementId,args);
                    }
                }
            }
        });

        return (T) proxyInstance;
    }


}

在这里我们其实需要判断改sql的类型,以方便我们去执行不同的sql。简单处理,判断没有返回参数的就是增删改操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值