2021-05-03 11:00  阅读(162)
文章分类:MyBatis 教程 文章标签:MyBatisMyBatis 教程
©  原文作者:一点教程 原文地址:http://www.yiidian.com/springmvc/

1 MyBatis内置数据源类型

MyBatis支持三种内置的数据源类型:

  1. Pooled:实现dataSource接口,并且使用了池的思想。
  2. UNPooled:同样也是实现了dataSource接口,但是该类型并没有使用池的思想。
  3. JNDI:采用服务器提供的JNDI技术实现,并且在不同服务器之间获取的连接池不一样。

202105031100233341.png

2 MyBatis数据源配置

下面是MyBatis的SqlMapConfig.xml配置中数据源的配置:

    
    <!-- 数据库连接池 -->
    <dataSource type="POOLED">
       <property name="driver" value="${jdbc.driver}" />
       <property name="url" value="${jdbc.url}" />
       <property name="username" value="${jdbc.username}" />
       <property name="password" value="${jdbc.password}" />
    </dataSource>

3 UNPooled

这种方式是不具备连接池的思想,如果频繁的创建和销毁连接对象,会影响程序的运行效率。

3.1 UNPooled配置

    
    <dataSource type="unpooled">
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEcndoing=utf8"/>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </dataSource>

3.2 UNPooled源码分析

1)获取连接的源码

    
    @Override
    public Connection getConnection() throws SQLException {
        return doGetConnection(username, password);
    }

2)接着再进入到doGetConnection()方法中:

    
    private Connection doGetConnection(String username, String password) throws SQLException {
      // 实例化一个集合
      Properties props = new Properties();
      if (driverProperties != null) {
        props.putAll(driverProperties);
      }
      // 判断用户名是否为空
      if (username != null) {
        props.setProperty("user", username);
      }
      // 判断密码是否为空
      if (password != null) {
        props.setProperty("password", password);
      }
      return doGetConnection(props);
    }

3)此时又返回一个doGetConnection(),这是重载的另一个方法。

    
    private Connection doGetConnection(Properties properties) throws SQLException {
      // 初始化
      initializeDriver();
      // 获取一个连接对象
      Connection connection = DriverManager.getConnection(url, properties);
      configureConnection(connection);
      return connection;
    }

4)第一行代码中,调用了initializeDriver()方法。

    
    private synchronized void initializeDriver() throws SQLException {
      if (!registeredDrivers.containsKey(driver)) {
        Class<?> driverType;
        try {
          if (driverClassLoader != null) {
              // 使用反射获取到连接驱动
            driverType = Class.forName(driver, true, driverClassLoader);
          } else {
            driverType = Resources.classForName(driver);
          }
          // DriverManager requires the driver to be loaded via the system ClassLoader.
          // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
          // 实例化连接驱动
            Driver driverInstance = (Driver)driverType.newInstance();
            // 注册驱动
          DriverManager.registerDriver(new DriverProxy(driverInstance));
          registeredDrivers.put(driver, driverInstance);
        } catch (Exception e) {
          throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
        }
      }
    }

大致流程:

  1. 在获取连接对象时,调用initializeDriver()方法判断是否已经注册连接驱动。
  2. 完成驱动注册,使用DriverManager.getConnection获取一个连接对象。
  3. 将连接对象交给configureConnection()方法,并设置自动提交事务,及事务的隔离级别。
    
    private void configureConnection(Connection conn) throws SQLException {
      if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
        conn.setAutoCommit(autoCommit);
      }
      if (defaultTransactionIsolationLevel != null) {
        conn.setTransactionIsolation(defaultTransactionIsolationLevel);
      }
    }

4 Pooled

连接池就是用于存储连接对象的一个容器。而容器就是一个集合,且必须是线程安全的,即两个线程不能拿到同一个连接对象。同时还要具备队列的特性:先进先出原则。

使用连接池的好处:避免频繁创建和关闭数据库连接造成的开销,节省系统资源。

4.1 Pooled配置

    
    <dataSource type="pooled">
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEcndoing=utf8"/>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </dataSource>

4.2 Pooled源码分析

1)获取连接源码

    
    @Override
    public Connection getConnection() throws SQLException {
      return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
    }

2)调用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.isEmpty()) {
            // 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);
              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()) {
                  try {
                    oldestActiveConnection.getRealConnection().rollback();
                  } catch (SQLException e) {
                    log.debug("Bad connection. Could not roll back");
                  }  
                }
                conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
                conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
                conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
                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) {
            // ping to server and check the connection is valid or not
            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 + poolMaximumLocalBadConnectionTolerance)) {
                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. 判断连接池中是否有空闲的连接对象,有则直接返回。
  2. 如果连接池没有空闲的连接,先判断活动连接池是否小于连接池承载的最大数量,小于则再创建新的连接对象。
  3. 但是如果连接池已经达到最大承载数量,那么在连接池中就把最先进来的连接(oldest)返回出去。

5 JNDI

JNDI(Java Naming and Directory Interface),是SUN公司推出的一套规范,属于JavaEE技术之一。目的是模仿windows系统中的注册表。

如果将类型设置成JNDI,MyBatis从应用服务器(如:Tomcat)配置好的JNDI数据源获取数据源连接。

注意:MyBatis需要使用JNDI作为数据源,必须为Web项目。

5.1 创建Web工程,导入依赖

    
    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
      <modelVersion>4.0.0</modelVersion>  
      <groupId>com.yiidian</groupId>  
      <artifactId>ch02_07_mybatis_datasource</artifactId>  
      <version>1.0-SNAPSHOT</version>  
      <packaging>war</packaging>
      <dependencies> 
        <!--mysql驱动-->  
        <dependency> 
          <groupId>mysql</groupId>  
          <artifactId>mysql-connector-java</artifactId>  
          <version>5.1.46</version> 
        </dependency>  
        <!-- mybatis依赖-->  
        <dependency> 
          <groupId>org.mybatis</groupId>  
          <artifactId>mybatis</artifactId>  
          <version>3.5.3</version> 
        </dependency>  
        <dependency> 
          <groupId>log4j</groupId>  
          <artifactId>log4j</artifactId>  
          <version>1.2.17</version> 
        </dependency>  
        <dependency> 
          <groupId>junit</groupId>  
          <artifactId>junit</artifactId>  
          <version>4.12</version> 
        </dependency>
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>servlet-api</artifactId>
          <version>2.5</version>
        </dependency>
      </dependencies> 
    </project>
    

5.2 创建和配置context.xml配置

在webapp目录建立META-INF目录,在里面建立context.xml文件

202105031100235002.png

内容如下:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
        <Resource
                name="jdbc/mybatis"
                type="javax.sql.DataSource"
                auth="Container"
                maxActive="20"
                maxWait="10000"
                maxIdle="5"
                username="root"
                password="root"
                driverClassName="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/mybatis"
        />
    </Context>

以上配置也可以配置在Tomcat的server.xml文件里面:

Tomcat的server.xml

    
    <Context>
    
    
      <Resource
                name="jdbc/mybatis"
                type="javax.sql.DataSource"
                auth="Container"
                maxActive="20"
                maxWait="10000"
                maxIdle="5"
                username="root"
                password="root"
                driverClassName="com.mysql.jdbc.Driver"
                url="jdbc:mysql://localhost:3306/mybatis"
        />
    
    </Context>

5.3 SqlMapConfig.xml配置

    
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <dataSource type="JNDI">
                <property name="data_source" value="java:comp/env/jdbc/mybatis"/>
            </dataSource>
        </environment>
    </environments>

5.4 编写Servlet调用Dao方法

    
    package com.yiidian.web;
    
    import com.yiidian.dao.CustomerDao;
    import com.yiidian.domain.Customer;
    import com.yiidian.utils.MyBatisUtils;
    import org.apache.ibatis.session.SqlSession;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.List;
    
    /**
     * 控制器
     * 一点教程网 - www.yiidian.com
     */
    public class CustomerServlet extends HttpServlet {
        @Override
        public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            this.doPost(req,resp);
        }
    
        @Override
        public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1.获取SqlSession对象
            SqlSession session = MyBatisUtils.getSession();
            //2.生成Dao代理对象
            CustomerDao customerDao = session.getMapper(CustomerDao.class);
            //3.调用方法
            List<Customer> list = customerDao.findAll();
            System.out.println(list);
    
            //4.关闭连接
            session.close();
        }
    }
    

5.5 MyBatisUtils工具类内容

    
    package com.yiidian.utils;
    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.IOException;
    import java.io.InputStream;
    /**
     * 工具类
     *一点教程网 - www.yiidian.com
     */
    public class MyBatisUtils {
        private static SqlSessionFactoryBuilder builder;
        private static SqlSessionFactory factory;
    
        /**
         * 初始化SqlSessionFactory
         */
        static {
            try {
                builder = new SqlSessionFactoryBuilder();
                InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
                factory = builder.build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取sqlSession
         */
        public static SqlSession getSession(){
            return factory.openSession();
        }
    }
    

5.6 部署到Tomcat,访问测试

http://localhost:8080/customer

202105031100236203.png

源码下载:https://pan.baidu.com/s/1stjv6RB_FmTGN2vmVWAonA

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> MyBatis 连接池
上一篇
MyBatis 一对多关系映射
下一篇
PageHelper分页插件