问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

mybatis 是在什么时候连接到数据库的,是在服务器启动时连接还是执行sql语句时候连接?

发布网友 发布时间:2022-04-07 23:05

我来回答

3个回答

懂视网 时间:2022-04-08 03:27

static void main(String[] args) throws Exception { String mybatisConfigPath = "config/mybatis/mybatis.xml"; InputStream inputStream = Resources.getResourceAsStream(mybatisConfigPath); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); int count = (Integer)sqlSession.selectOne("org.alien.mybatis.samples.mapper.AuthorMapper.getAllAuthorsCount"); System.out.println(count); }
  • 上面是一段Mybatis执行代码
  • 我们可以将Mybatis连接数据库的信息有意写错
  • 再DEBUG模式下一步一步调试看哪一步会抛异常
  • 抛异常的那一步就是真正获取数据库连接的一步
  • 异常信息:
    技术分享

    深入源码

    这里简单提一下、具体后面会有。最有迷惑性的是觉得在openSession()的时候会获取数据库连接、其实不然:
    openSession()最终只是返回一个操作数据库的会话、并不包含数据库连接,DefaultSqlSession(这个是Mybatis初始化的时候返回的一个SqlSession)中的方法:

     private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
     Transaction tx = null;
     try {
     final Environment environment = configuration.getEnvironment();
     final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
     tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
     final Executor executor = configuration.newExecutor(tx, execType);
     return new DefaultSqlSession(configuration, executor, autoCommit);
     } catch (Exception e) {
     closeTransaction(tx); // may have fetched a connection so lets call close()
     throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
     } finally {
     ErrorContext.instance().reset();
     }
     }
  • 主要装配DefaultSqlSession中执行Sql的Excutor、后面关于一个Sql完整的执行过程会有对其的详细分析
  • 显然真正获取数据库连接的操作是在sqlSession.selectOne("org.alien.mybatis.samples.mapper.AuthorMapper.getAllAuthorsCount");进行的。

    获取数据库连接

    在真正的获取数据库连接代码之前、还有许多为sql执行而生的代码、这里暂时忽略或者一些必要的说明、主要重心放在如何获取数据库连接。
    书接上回、从前面执行sql代码开始:

    int count = (Integer)sqlSession.selectOne("org.alien.mybatis.samples.mapper.AuthorMapper.getAllAuthorsCount");

    下图是上面代码一系列方法调用过程:

    技术分享

    经过一系列调用到SimpleExecutor——》doQuery():

     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();
     //RoutingStatementHandler
     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
     //MappedStatement、这一句是关键
     stmt = prepareStatement(handler, ms.getStatementLog());
     /*
     *参数:
     * stmt: PreparedStatementLogger
     * resultHandler: null 
     */
     return handler.<E>query(stmt, resultHandler);
     } finally {
     closeStatement(stmt);
     }
     }
    
  • 上面经过一系列跳转之后各个引用的实例我加了注释、有兴趣的可以自己跟一下、有时候可能要多跟几遍
  • 关键代码stmt=prepareStatement(handler,ms.getStatementLog());、这一句根据方法名就能猜测、是根据Connection来获取执行Sql的PrepareStatement
  • 但是到现在为止我们都没有看到方法的参数中有关于数据库连接的、在此方法中
  • SimpleExecutor——》prepareStatement();

     private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
     Statement stmt;
     //获取数据库连接。statementLog:org.apache.ibatis.loggin.slf4j.Slf4Impl
     Connection connection = getConnection(statementLog);
     //获取执行Sql的Statement——PrepareStatementLogger
     //PrepareStatementLogger是PrepareStatement的代理、多了对使用Mybatis执行sql语句时记录sql语句的功能
     stmt = handler.prepare(connection);
     //将执行Sql需要的参数设置到PrepareStatement中。
     handler.parameterize(stmt);
     return stmt;
     }
    

    其他的不关注、这里只看数据库连接代码:Connection connection = getConnection(statementLog);BaseExecutor——》getConnection():

     protected Connection getConnection(Log statementLog) throws SQLException {
     //如果关于数据库连接的日志记录级别是DEBUG级别、则为获取的Connection进行代理、新增日志记录功能、这里不是重点。
     if (statementLog.isDebugEnabled()) {
     return ConnectionLogger.newInstance(connection, statementLog, queryStack);
     } else {
     return connection;
     }
     }
  • Mybatis中关于事务配置项的值是”JDBC”、所以从Mybatis深入之事务管理 知道这里的transaction其实是:JdbcTransaction
  • 最终到JdbcTransaction获取连接的方法中
  • JdbcTransaction——》openConnection()

     protected void openConnection() throws SQLException {
     if (log.isDebugEnabled()) {
     log.debug("Opening JDBC Connection");
     }
     connection = dataSource.getConnection();
     if (level != null) {
     connection.setTransactionIsolation(level.getLevel());
     }
     setDesiredAutoCommit(autoCommmit);
     }
  • 上面的dataSource从 Mybatis深入之DataSource实例化过程 知道当使用数据库连接池的时候实例化的是PooledDataSource
  • PooledDataSource——》getConnection():

     public Connection getConnection() throws SQLException {
    
    /*
     * 为理解方便、将原来代码拆分如下:
     */
     PooledConnection pooledConnection = popConnection(dataSource.getUsername(), dataSource.getPassword());
     Connection connection = pooledConnection.getProxyConnection();
     return connection ;
    
    //return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
     }
    
  • 从拆分代码看分两步
  • 获取数据库真正连接
  • 获取真正数据库连接的代理类作为最终返回结果、至于代理是做什么、后面继续
  • PooledDataSource——》popConnection():

     private PooledConnection popConnection(String username, String password) throws SQLException {
     boolean countedWait = false;
     PooledConnection conn = null;
     long t = System.currentTimeMillis();
     int localBadConnectionCount = 0;
    
     while (conn == null) {
     synchronized (state) {
     if (state.idleConnections.size() > 0) {
      // Pool has available connection
      conn = state.idleConnections.remove(0);
      if (log.isDebugEnabled()) {
      log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
      }
     } else {
      // Pool does not have available connection
      if (state.activeConnections.size() < poolMaximumActiveConnections) {
      // Can create new connection
      conn = new PooledConnection(dataSource.getConnection(), this);
      @SuppressWarnings("unused")
      //used in logging, if enabled
      Connection realConn = conn.getRealConnection();
      if (log.isDebugEnabled()) {
      log.debug("Created connection " + conn.getRealHashCode() + ".");
      }
      } else {
      // Cannot create new connection
      PooledConnection oldestActiveConnection = state.activeConnections.get(0);
      long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
      if (longestCheckoutTime > poolMaximumCheckoutTime) {
      // Can claim overdue connection
      state.claimedOverdueConnectionCount++;
      state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
      state.accumulatedCheckoutTime += longestCheckoutTime;
      state.activeConnections.remove(oldestActiveConnection);
      if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
      oldestActiveConnection.getRealConnection().rollback();
      }
      conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
      oldestActiveConnection.invalidate();
      if (log.isDebugEnabled()) {
      log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
      }
      } else {
      // Must wait
      try {
      if (!countedWait) {
       state.hadToWaitCount++;
       countedWait = true;
      }
      if (log.isDebugEnabled()) {
       log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
      }
      long wt = System.currentTimeMillis();
      state.wait(poolTimeToWait);
      state.accumulatedWaitTime += System.currentTimeMillis() - wt;
      } catch (InterruptedException e) {
      break;
      }
      }
      }
     }
     if (conn != null) {
      if (conn.isValid()) {
      if (!conn.getRealConnection().getAutoCommit()) {
      conn.getRealConnection().rollback();
      }
      conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
      conn.setCheckoutTimestamp(System.currentTimeMillis());
      conn.setLastUsedTimestamp(System.currentTimeMillis());
      state.activeConnections.add(conn);
      state.requestCount++;
      state.accumulatedRequestTime += System.currentTimeMillis() - t;
      } else {
      if (log.isDebugEnabled()) {
      log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
      }
      state.badConnectionCount++;
      localBadConnectionCount++;
      conn = null;
      if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
      if (log.isDebugEnabled()) {
      log.debug("PooledDataSource: Could not get a good connection to the database.");
      }
      throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
      }
      }
     }
     }
    
     }
    
     if (conn == null) {
     if (log.isDebugEnabled()) {
     log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
     }
     throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
     }
    
     return conn;
     }
    
  • 先解释一下逻辑、再关注具体的数据库连接方法
  • 1. 先看是否有空闲(idle)状态下的PooledConnection对象,如果有,就直接返回一个可用的PooledConnection对象;否则进行第2步。
    
    2. 查看活动状态的PooledConnection池activeConnections是否已满;如果没有满,则创建一个新的PooledConnection对象,然后放到activeConnections池中,然后返回此PooledConnection对象;否则进行第三步;
    
    3. 看最先进入activeConnections池中的PooledConnection对象是否已经过期:如果已经过期,从activeConnections池中移除此对象,然后创建一个新的PooledConnection对象,添加到activeConnections中,然后将此对象返回;否则进行第4步。
    
    4. 线程等待,循环2步

    具体的创建数据库连接代码conn = new PooledConnection(dataSource.getConnection(), this);

  • 上面代码中的dataSource为UnpooledDataSource、可以从 Mybatis深入之DataSource实例化过程 了解原因。
  • 所以先要看UnpooledDataSource——getConnection() 经过一系列跳转到同类如下方法:

     private Connection doGetConnection(Properties properties) throws SQLException {
     initializeDriver();
     Connection connection = DriverManager.getConnection(url, properties);
     configureConnection(connection);
     return connection;
     }
    
  • 上面我们可以看到很熟悉的加载驱动、获取数据库连接
  • 补充

    其实关于数据库部分还有很多要写的、比如数据库连接池工作原理、数据库连接何时关闭。这里暂不准备一次将所有的东都放在一起。觉得分开点更容易说清楚理解起来不是那么费劲。
    后面会有一篇Mybatis数据库连接池原理来分析它。

    更多内容Mybatis 目录

    Mybatis深入之获取数据库连接

    标签:mybatis   获取数据   数据库连接池   数据库连接   

    热心网友 时间:2022-04-08 00:35

    一般用mybatis都是用数据库连接池c3p0等,连接池是在服务器启动的时候先创建一些连接,用的时候直接从连接池中取出来用,用完了,返回给连接。连接不会关闭,一直开着,这样能减少新建连接,连接关闭带来的资源消耗

    热心网友 时间:2022-04-08 01:53

    在使用SqlSessionFactory创建回话的时候会连接数据库,即SqlSessionFactory.openSession()。初始化SqlSessionFactory只是将配置文件初始化到相应的类中。
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    如何查被录取到的专业 怎样查被录取的专业 录取后怎样查询录取的专业 录取专业怎么查 已被录取怎么查专业 ghs网络语什么意思_ghs网络语意思出处含义介绍 纸箱企业管理软件 ghs什么意思网络(ghs什么意思网络用语) 《喜羊羊与灰太狼》大结局 0与任何数相加都得原数吗? 建行信用卡未激活冻结了怎么解冻 信用卡拿到未激活就被冻结了是怎么回事? 平安银行信用卡被冻结了需要四天之后才能激活吗 建行信用卡被冻结怎么办 我中国银行信用卡被当地分行冻结了。怎样才可以开通啊 信用卡冻结后还可以申请开通吗? 中银信用卡被冻结了怎么办 自己用榨汁机榨果汁喝,最近听朋友说原汁机不错,那什么牌子的原汁机出汁率很高呢? 原汁机鲜榨果汁怎么做 从网上买了台夏普原汁机,想用它来榨苹果汁喝,请问榨的时候还需要向里面加水吗? 公交卡在哪里办需要什么证件学生 泰州市老年公交卡在哪年检泰州市老年公交卡在什么路办理? 泰州高港区胡庄镇老年人去哪里办理免费公交车卡,要带哪些证件? 泰州普通公交卡在什么地方办,我要详细的所有的地点,谢谢!!! 泰州的公交卡到哪里办? 泰州公交卡在哪里办啊? 泰州的公交一卡通怎么办?在哪里办,需要什么证件,大概多少工本费?谢谢 泰州哪里可以办理公交卡 泰州一卡通办理地点 小米电视哪个型号的性能最好? =IFERROR(VLOOKUP(E5,进出存!C:P,16,0),0)不显示函数数值 excel函数 if错误和0都不显示 你给狗狗杀蜱虫买的是什么药啊?能不能告诉我 给狗驱蜱虫用什么药便宜 excel 用if函数计算如何错误值不显示 我表格使用了IFERROR函数,关联的其他表格,之前能用,但在插入新的行之后,下拉复制公式就不显示 EXCEL中如何将计算出来的错误值变成0或不显示 小狗身上有蜱虫用什么药可以杀死 微信我把对方删除了,他总是发验证信息给我,我如何拉黑他? 在excel中IF函数只显示公式不显示结果 狗狗身上的蜱虫用什么常用的药除掉啊 iferror函数是为什么查找不出来 剪映创作者招募每次不通过怎么办? 每次上传自己制作的视频都没通过,请问要上传怎么样的作品才能通过急_百度问一问 我家狗身上有蜱虫怎么办? 什么药比较管用啊? 急急急急急急急急急急~~~~~~~ 狗狗身上有蜱虫 怎么办。要用什么药? EXCEL表格里如何让没计算出结果的数不显示呢? 狗狗身上有好多蜱虫,该怎么治疗? 狗身上有蜱虫用什么药 给狗去蜱虫的药 银行理财产品利息按360天计算,还是按365天计算?有没有法律规定?