专栏首页LanceToBigDataJDBC(三)数据库连接池(DBCP、C3P0)

JDBC(三)数据库连接池(DBCP、C3P0)

前言

  这段时间状态有一点浮躁,希望自己静下心来。还有特别多的东西还没有学懂。需要学习的东西非常的多,加油!

一、JDBC复习

  Java Data Base Connectivity,java数据库连接,在需要存储一些数据,或者拿到一些数据的时候,就需要往数据库里存取数据。那么java如何连接数据库呢?需要哪些步骤?

1.1、注册驱动

  1)什么是驱动

     驱动就是JDBC实现类,通俗点讲,就是能够连接到数据库功能的东西就是驱动,由于市面上有很多数据库,Oracle、MySql等等,所以java就有一个连接数据库的实现规

    范接口,定义一系列的连接数据库接口(java.sql.Driver接口),但是不提供实现,而每个数据库厂家来提供这些接口的具体实现,这样一来,不管使用的是什么数据库,我

    们开发者写的代码都是相同的,就不必因为数据库的不同,而写法不同,唯一的不同就是数据库驱动不一样,使用mysql,那么就必须使用mysql的驱动,使用Oracle就必

    须使用oracle的驱动实现类。

    看下面mysql连接数据的原理图,看看驱动是在哪里,起什么作用?

  2)DriverManager,一个工具类,是用于操作管理JDBC实现类的

 原始写法:DriverManager.register(new Driver());  //因为使用的是MySql,所以在导包时就需要导入com.mysql.jdbc.Driver

 现在写法:Class.forName("com.mysql.jdbc.Driver");  //不用导包,会执行com.mysql.jdbc.Driver类中的静态代码块,其静态代码块的内容为
            static {
                try {                      
                 java.sql.DriverManager.registerDriver(new Driver());
                    } catch (SQLException E) {
                    throw new RuntimeException("Can't register driver!");
                             }
                        }  

  分析: 

   会发现第二种加载驱动的方法的底层其实就是第一种加载驱动。为什么要这样呢?原因很简单, 第一种是硬编程,直接将数据库驱动给写死了,无法扩展,如果使用第一

   种,那么连接的数据库只能是mysql,因为导包导的是mysql的驱动包,如果换成Oracle,就会报错,需要在代码中将Oracle的驱动包导入,这样很麻烦,而第二种写法就不

   一样了,第二种是使用的字符串方法注册驱动的,我们只需要将该字符串提取到一个配置文件中,以后想换成oracle数据库,只需要将该字符串换成oracle驱动的类全名即可

   ,而不需要到代码中去修改什么东西。

1.2、获取连接

  使用DriverManage来获得连接,因为DriverManager是驱动实现类的管理者

  Connection conn = DriverManager.getConnection(url,user,password);

    url:确定数据库服务器的位置,端口号,数据库名

      jdbc:mysql://localhost:3306/db

    user:登录名称,默认root

    password:密码,默认root   

  这里只是说mysql,别的数据库,url格式就不同了。

    MySQL    jdbc:mysql://localhost:3306/db    默认端口是3306,粗体为连接时使用的数据库名

    Oracle     jdbc:oracle:thin:@localhost:1521:db  默认端口号1521

    DB2      jdbc:db2://localhost:6789/db      默认端口号6789

    SQLServer  jdbc:microsoft:sqlserver://localhost:1433;databaseName=db  默认端口号1433

    SQLServer 2005  jdbc:sqlserver://localhost:1433;databaseName=db  默认端口号1433

1.3、获取执行sql对象,PreparedStatement对象 

  通过Connection对象获取Statement或者PraparedStament对象(使用它)处理sql

  1)Statement

    Statement st = conn.createStatement();  //获取sql语句执行对象

    st.excuteUpdate(sql);  //执行增删改语句

    st.excuteQuery(sql);  //执行查询语句      

    sql语句必须是完整的。

  2)PraparedStatment

    sql语句可以不是完整的,可以将参数用?替代,然后在预编译后加入未知参数

    PraparedStatment ps = conn.prapareStatement(sql);  //获取sql语句执行对象praparedStatment

    赋值

      ps.setInt(Index,value);  ps.setString(index,value);  //可以设置很多中类型,index从1开始,代表sql语句中的第几个未知参数,

      ps.excuteUpdate();  //执行增删改语句

      ps.excuteQuery(sql);  //执行查询语句

  这两个的区别,常使用的是PraparedStatment对象,因为它可以预编译,效率高,可以设置参数等等优点。

1.4、获取结果集对象

  int count = ps.excuteUpdate();   //执行增删改的sql语句时,返回一个int类型的整数,代表数据库表影响的行数,

  Result result = ps.excuteQuery();  //执行查询sql语句时,返回一个结果集对象,该对象装着所有查询到的数据信息,一行一行的存储数据库表信息。

1.5、处理结果集

  对查询到的Result结果进行处理,拿到所有数据,并封装成对象。

        while(rs.next()){

          获取行数据的第一种方式
          rs.getString(index);//index代表第几列,从1开始

          获取行数据的第二中方式
          rs.getString(string);  //string:代表字段名称。

        }

  总结:java的JDBC就分为5步,4个属性

    属性:driver、url、user、password

    五步:

      注册驱动、获取连接、获取执行sql语句对象、获取结果集对象、处理结果。

二、JDBC的CURD操作

  创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作

  查询所有(读取Retrieve)

2.1、查询所有记录读取(Retrieve)

@Test
    public void findAll() throws Exception{
        //1 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2 获得连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3语句执行者,sql语句
        Statement st = conn.praparedStatement("select * from t_user");
        //4 执行查询语句
        ResultSet rs = st.executeQuery();
        //5处理数据
        // * 如果查询多个使用,使用while循环进行所有数据获取
        // * 技巧:如果查询结果最多1条,使用  if(rs.next()) {  查询到了 } else {  没有数据 }
        while(rs.next()){
            int id = rs.getInt(1);
            String username = rs.getString(2);
            String password =rs.getString(3);
            System.out.print(id + ", ");
            System.out.print(username + ", ");
            System.out.println(password);
        }
        //6释放资源
        rs.close();
        st.close();
        conn.close();
        
    }

2.2、增加操作(创建Create)  

@Test
    public void save() throws Exception{
        //1 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2 获得连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3语句执行者
        Statement st = conn.praparedStatement("insert into t_user(username,password) values(?,?)");
                //3.1赋值
                st.setString(1,"xiaoming");
                st.setString(2,"123");
        //4 执行DML语句
        int r = st.executeUpdate();
        
        //5处理数据
        System.out.println(r);
        
        //6释放资源
        //rs.close();
        st.close();
        conn.close();
    }

2.3、更新操作 (Update)

@Test
    public void update() throws Exception{
        //1 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2 获得连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3语句执行者
        Statement st = conn.praparedStatement("update t_user set username = ? where id = ? ");
                //3.1赋值参数
                st.setString(1,"xiaoye");
                st.setInt(2,2);
        //4 执行DML语句
        int r = st.executeUpdate();
        
        //5处理数据
        System.out.println(r);
        
        //6释放资源
        //rs.close();
        st.close();
        conn.close();
}

2.4、删除操作(delete)

@Test
    public void delete() throws Exception{
        //1 注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2 获得连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        //3语句执行者
        Statement st = conn.praparedStatement("delete from t_user where id = ?");
                //3.1赋值参数
                st.setInt(1,2);
        //4 执行DML语句
        int r = st.executeUpdate();
        
        //5处理数据
        System.out.println(r);
        
        //6释放资源
        //rs.close();
        st.close();
        conn.close();
}

  也可以用我前面写的JDBC连接的工具类去连接!

三、数据库连接池

  在上面,我们在进行CRUD时,一直重复性的写一些代码,比如最开始的注册驱动,获取连接代码,一直重复写,通过编写一个获取连接的工具类后,解决了这个问题,但是又

  会出现新的问题,每进行一次操作,就会获取一个连接,用完之后,就销毁,就这样一直新建连接,销毁连接,新建,销毁,连接Connection 创建与销毁 比较耗时的。所以应

  该要想办法解决这个问题?

  解决方法:

    连接池就是为了解决这个问题而出现的一个方法,为了提高性能,开发连接池,连接池中一直保持有n个连接,供调用者使用,调用者用完返还给连接池,继续给别的调用

    者使用,比如连接池中一开始就有10个连接,当有5个用户拿走了5个连接后,池中还剩5个,当第6个用户在去池中拿连接而前面5个连接还没归还时,连接池就会新建一个

    连接给第六个用户,让池中一直能够保存最少5个连接,而当这样新建了很多连接后,用户归还连接回来时,会比原先连接池中的10个连接更多,连接池就会设置一个池中

    最大空闲的连接数,如果超过了这个数,就会将超过的连接给释放掉,连接池就是这样工作的。

3.1、连接池概述

  数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避

  免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

3.2、比较应用程序直接获取连接和使用连接池

  1)应用程序直接获取连接

    缺点:用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。

          假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、宕机。

  2)使用连接池连接

    目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。

四、常用的数据库连接池  

  现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,

  数据源中都包含了数据库连接池的实现。   也有一些开源组织提供了数据源的独立实现:     DBCP 数据库连接池     C3P0 数据库连接池 实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。

  DBCP、C3P0、tomcat内置连接池(JNDI)是我们开发中会用到的。

4.1、DBCP连接池

  1)概述

    DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:       Commons-dbcp.jar:连接池的实现       Commons-pool.jar:连接池实现的依赖库     Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

  1)获取连接的两种方式

    两种方式获得连接,使用配置文件,不使用配置文件

    1.1)不使用配置文件,自己手动设置参数

       第一:导包

      第二:测试例子

package com.zyh.util;

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

import org.apache.commons.dbcp.BasicDataSource;


public class TestDBCP {

    public static void main(String[] args) {
        Connection conn = TestDBCP.getConnection();
        try {
            String sql = "select id,name,price from book";
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            while(rs.next()){
                String id = rs.getString(1);
                String name = rs.getString(2);
                double price = rs.getDouble(3);
                System.out.println("id="+id+",name="+name+",price="+price);
                System.out.println("-------------------------");
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
    }
    
    public static Connection getConnection(){
        //创建核心类
                BasicDataSource bds = new BasicDataSource();
                //配置4个基本参数
                bds.setDriverClassName("com.mysql.jdbc.Driver");
                bds.setUrl("jdbc:mysql:///book");
                bds.setUsername("root");
                bds.setPassword("654321");
                
                //管理连接配置
                bds.setMaxActive(50); //最大活动数
                bds.setMaxIdle(20);  //最大空闲数
                bds.setMinIdle(5);  //最小空闲数
                bds.setInitialSize(10);  //初始化个数
                
                //获取连接
                try {
                     Connection conn = bds.getConnection();
                     return conn;
                } catch (SQLException e) {
                    throw new RuntimeException(e);
                    }
    }

}

    1.2)使用配置文件,参数写入配置文件中即可,也就是通过配置文件来配置驱动、用户名、密码、等信息

      第一:导包

      和上面的一样的

      第二:导入配置文件dbcpconfig.properties

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/book
username=root
password=654321

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000


#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

      第三:获取连接,测试例子

package com.zyh.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class TestDBCPPro {

    public static void main(String[] args) {
        Connection conn = TestDBCPPro.getConnection();
        try {
            String sql = "select id,name,price from book";
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            while(rs.next()){
                String id = rs.getString(1);
                String name = rs.getString(2);
                double price = rs.getDouble(3);
                System.out.println("id="+id+",name="+name+",price="+price);
                System.out.println("-------------------------");
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection(){
         //通过类加载器获取指定配置文件的输入流,TestDBCPPro是一个类名
        InputStream in = TestDBCPPro.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
        Properties properties = new Properties();
        try {
                properties.load(in);
                //加载配置文件,获得配置信息
                DataSource ds = BasicDataSourceFactory.createDataSource(properties);
                Connection conn = ds.getConnection();
                return conn;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    

}

    1.3)编写一个数据源工具类

package com.zyh.util;


import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import javax.imageio.stream.FileImageInputStream;
import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtils {
    private static DataSource ds = null;
    static{
        Properties prop = new Properties();
        try {
            prop.load(DBCPUtils.class.getClassLoader().getResourceAsStream("dbcpconfig.properties"));//根据DBCPUtil的classes的路径,加载配置文件
            ds = BasicDataSourceFactory.createDataSource(prop);//得到一个数据源 
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化错误,请检查配置文件");
        }
    }
    
    public static Connection getConnection(){
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服务器忙。。。");
        }
    }
    
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        //关闭资源
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    rs = null;
                }
                if(stmt!=null){
                    try {
                        stmt.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    stmt = null;
                }
                if(conn!=null){
                    try {
                        conn.close();//关闭
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    conn = null;
                }
    }
    
}

4.2、C3P0

  1)导包

  2)从配置信息中获取  配置文件必须为xml(c3p0-config.xml)

<c3p0-config>
    <!-- 默认配置,如果没有指定则使用这个配置 -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/book</property>
        <property name="user">root</property>
        <property name="password">654321</property>
    
        <property name="checkoutTimeout">30000</property>
        <property name="idleConnectionTestPeriod">30</property>
        <property name="initialPoolSize">10</property>
        <property name="maxIdleTime">30</property>
        <property name="maxPoolSize">100</property>
        <property name="minPoolSize">10</property>
        <property name="maxStatements">200</property>
        <user-overrides user="test-user">
            <property name="maxPoolSize">10</property>
            <property name="minPoolSize">1</property>
            <property name="maxStatements">0</property>
        </user-overrides>
    </default-config> 
    <!-- 命名的配置 -->
    <named-config name="jxpx">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/myums</property>
        <property name="user">root</property>
        <property name="password">root</property>
    <!-- 如果池中数据连接不够时一次增长多少个 -->
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">20</property>
        <property name="minPoolSize">10</property>
        <property name="maxPoolSize">40</property>
        <property name="maxStatements">0</property>
        <property name="maxStatementsPerConnection">5</property>
    </named-config>
</c3p0-config>

    从配置文件中看,需要注意一个地方,一个是default-config,一个是name-config,两者都区别在于创建核心类对象时,如果将name-config作为参数传进去,

    那么将会调用name-config下的配置信息,否则将调用default-config下的配置信息,

    两种方式使用c3p0,加参数,使用named-config 的配置信息,不加参数,自动加载配置信息,加载的是default-config中的信息。

        //1 c3p0...jar 将自动加载配置文件。规定:WEB-INF/classes (src)  c3p0-config.xml,也就是将配置文件放在src下就会自动加载。
         //ComboPooledDataSource dataSource = new ComboPooledDataSource(); //自动从配置文件 <default-config>
         ComboPooledDataSource dataSource = new ComboPooledDataSource(); //手动指定配置文件 <named-config name="jxpx">
         Connection conn = dataSource.getConnection();
         System.out.println(conn);

  3)编写一个数据源工具类

  这个工具类的xml是:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/book/property>
    <property name="user">root</property>
    <property name="password">654321</property>
    <property name="initialPoolSize">10</property>
    <property name="maxIdleTime">30</property>
    <property name="maxPoolSize">100</property>
    <property name="minPoolSize">10</property>

  </default-config>

</c3p0-config>

  工具类

package com.zyh.util;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Util {
    //得到一个数据源
    private static DataSource dataSource = new ComboPooledDataSource();
    
    //从数据源中得到一个连接对象
    public static Connection getConnection(){
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服务器错误");
        }
    }
    
    public static void release(Connection conn,Statement stmt,ResultSet rs){
        //关闭资源
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    rs = null;
                }
                if(stmt!=null){
                    try {
                        stmt.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    stmt = null;
                }
                if(conn!=null){
                    try {
                        conn.close();//关闭
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    conn = null;
                }
    }
    
}

4.3、用JavaWeb服务器管理数据源:Tomcat

  开发JavaWeb应用,必须使用一个JavaWeb服务器,JavaWeb服务器都内置数据源。   Tomcat:(DBCP) 数据源只需要配置服务器即可。   配置数据源的步骤:     1)拷贝数据库连接的jar到tomcatlib目录下     2)配置数据源XML文件       a)如果把配置信息写在tomcat下的conf目录的context.xml中,那么所有应用都能使用此数据源。       b)如果是在当前应用的META-INF中创建context.xml, 编写数据源,那么只有当前应用可以使用。

<Context>
  <Resource name="jdbc/datasource" auth="Container"
            type="javax.sql.DataSource" username="root" password="654321"
            driverClassName="com.mysql.jdbc.Driver" 
          url="jdbc:mysql://localhost:3306/book"
            maxActive="8" maxIdle="4"/>
</Context>

  在servlet中进行测试:

  Context initCtx = new InitialContext();
  Context envCtx = (Context) initCtx.lookup("java:comp/env");  //path
  dataSource = (DataSource)envCtx.lookup("jdbc/datasource");   //context.xml中resource的name

  注意:此种配置下,驱动jar文件需放置在tomcat的lib下  

  扩展:

    JNDI技术介绍:

      1)JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包。       2)这套API的主要作用在于:

        它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。       3)其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaSE(八)集合之Set

    今天这一篇把之前没有搞懂的TreeSet中的比较搞得非常的清楚,也懂得了它的底层实现。希望博友提意见! 一、Set接口 1.1、Set集合概述   Set集合:...

    用户1195962
  • MySQL(九)之数据表的查询详解(SELECT语法)二

    上一篇讲了比较简单的单表查询以及MySQL的组函数,这一篇给大家分享一点比较难得知识了,关于多表查询,子查询,左连接,外连接等等。希望大家能都得到帮助! 在开始...

    用户1195962
  • JDBC(二)之JDBC处理CLOB和BLOB及事务与数据库元数据获取

    前面大概介绍了JDBC连接数据库的过程,以及怎么操作数据库,今天给大家分享JDBC怎么处理CLOB和BLOB存储图片的事情,以及JDBC怎么去处理事务。怎么在插...

    用户1195962
  • 【JMeter系列-5】JMeter操作Mysql数据库

    JMeter连接mysql数据库是很方便的,下面就演示一下具体的操作。 首先,在本地安装好mysql服务器,建立库和表,并准备数据以供测试使用。

    云深i不知处
  • 自动化测试最佳实践(一):从纺锤模型到金字塔模型

    开篇我们先简要介绍一些近几年在企业开发中出现的重要概念,以便引入持续测试的主旨。这些概念中最重要的两个便是DevOps和微服务。两者都是目前软件开发中的最佳实践...

    宜信技术学院
  • 2017 热门开源自动化测试框架优缺点对比

    时间一晃已来到 2017 年的最后一个季度,TestProject 对比了在今年比较热门的 7 款开源自动化测试框架的优缺点,以帮助你选择适合自己的测试框架。

    Debian社区
  • Android实现屏幕截图并保存截图到指定文件

    砸漏
  • 7款开源自动化测试框架优缺点对比

    1. Robot Framework Robot Framework(RF)是用于验收测试和验收测试驱动开发(ATDD)的自动化测试框架。 基于 Python ...

    企鹅号小编
  • 你选择红色药丸还是蓝色药丸?

    《黑客帝国》中有一个经典桥段,墨菲斯给主人公尼奥两片药丸:蓝色代表「从梦中醒来,沉沦虚幻世界,认为看到的只是做了假梦」,红色代表「跟我前进,领你去看真相」。尼奥...

    臭豆腐
  • H5 秒开方案大全

    http://www.alloyteam.com/2019/10/h5-performance-optimize/

    刘小夕

扫码关注云+社区

领取腾讯云代金券