1 JDBC概述

Java DataBase Connectivity,Java数据库连接,一种执行SQL的Java API,为多种关系数据库提供统一访问规范,由一组Java语言编写的类和接口组成。
数据库驱动:各个数据库生产商提供的JDBC实现类。使用统一的JDBC规范,不用专门去了解各个数据库的驱动API。
JDBC 可做三件事:与数据库建立连接、发送SQL、处理结果。

2 JDBC常用方法及增删改查

2.1 JDBC的开发步骤
先导入对应数据库的jar包 mysql-connector-java-5.0.8-bin.jar
  创建lib目录,用于存放当前项目需要的所有jar包
  选择jar包,右键执行build path / Add to Build Path

  1. 加载驱动
  2. 获得连接
  3. 获得语句执行平台statement
  4. 执行sql
  5. 处理结果
  6. 释放资源
 import org.junit.Test;
 public class JDBCDemo1 {
      @Test
      public void demo1() throws Exception{
         // 1.加载驱动
          Class.forName("com.mysql.jdbc.Driver");
          // 2.获得连接
          Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test3", "root", "abc");       //url如果连接的是本机的路径,可以简化为jdbc:mysql:///web_test3 (3个///)
          // 3.基本操作:执行SQL
          // 3.1获得执行SQL语句的对象
          Statement statement = conn.createStatement();
          // 3.2编写SQL语句:
          String sql = "select * from user";
          // 3.3执行SQL:
          ResultSet rs = statement.executeQuery(sql);
          // 3.4遍历结果集:
          while(rs.next()){
              System.out.print(rs.getInt("id")+" ");
              System.out.print(rs.getString("username")+" ");
              System.out.print(rs.getString("password")+" ");
              System.out.print(rs.getString("nickname")+" ");
              System.out.print(rs.getInt("age"));
              System.out.println();
          }
          // 4.释放资源
          rs.close();
          statement.close();
          conn.close();
      }
  }

2 JDBC常用类/接口:

DriverManager类:注册JDBC驱动,获取Connection对象(数据库链接对象,每个Connection代表一个物理连接会话)。
Class.forName("com.mysql.jdbc.Driver");
Connection接口:获取执行SQL的Statement对象,并包含多个用于控制事务的方法。

Statement createStatement() throws SQLException;              //返回一个Statement对象,用来将SQL语句发送到数据库;
PreparedStatement prepareStatement(String sql)throws SQLException;  //返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译;
CallableStatement prepareCall(String sql) throws SQLException;     //返回CallableStatement对象,用于调用存储过程。
   //以上都返回用于执行sql语句的Statement对象,PreparedStatement和CallableStatement是Statement的子类,只有获得了Statement之后才可以执行sql语句;

void setAutoCommit(boolean autoCommit) throws SQLException;  //关闭自动提交,打开事务;
void rollback() throws SQLException;               //回滚事务,并释放Connection对象当前持有的数据库锁;
void commit() throws SQLException;               //提交事务,并释放Connection对象当前持有的数据库锁;
void setTransactionIsolation(int level) throws SQLException;      //设置事务的隔离级别;
Savepoint setSavepoint() throws SQLException;          //创建一个保存点;
Savepoint setSavepoint(String name) throws SQLException;     //以指定名字来创建一个保存点;
void rollback(Savepoint savepoint) throws SQLException;      //将事务回滚到指定的保存点;

Statement接口:用于执行静态SQL语句并返回它所生成结果的对象。

 ResultSet executeQuery(String sql) throws SQLException;//执行查询语句,返回查询结果对应ResultSet对象。该方法只能用于执行查询语句。
 int executeUpdate(String sql) throws SQLException;//执行inset/update/delete语句,返回受影响的行数;也可以执行create/alter/drop/truncate语句,并返回0;
 boolean execute(String sql) throws SQLException;//执行任意sql语句。若执行后第一个结果为ResultSet对象,则返回true;
 //若执行后第一个结果为受影响的行数或无结果,则返回false;

 void addBatch(String sql);//将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中。
 void clearBatch();//清空此Statement对象当前的SQL命令列表。
 int[] executeBatch();//将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。

注意:如果 addBatch() -> executeBatch() 很慢,需要启动批处理操作rewriteBatchedStatements=true,即在数据库连接URL后面加上这个参数:String dbUrl =  "jdbc:mysql://localhost:3306/User? rewriteBatchedStatements=true";该参数将多条sql分成若干个报文发送到mysql服务器,并在最后加上commit操作。

ResultSet接口:结果集对象,包含查询结果,可以通过列索引或列名获得列数据。

boolean next();//将光标从当前位置向前移动一行
int     getInt(int columnIndex);//获取结果集当前行指定列
int     getInt(String columnLabel);//获取结果集当前行指定列
long    getLong(int columnIndex);//获取结果集当前行指定列
long    getLong(String columnLabel);//获取结果集当前行指定列
String  getString(int columnIndex);//获取结果集当前行指定列
String  getString(String columnLabel);//获取结果集当前行指定列

//示例代码:
while(resultSet.next()){
    System.out.println(resultSet.getInt("id"))
    System.out.println(resultSet.getString("ename"))
    System.out.println(resultSet.getString("nickname"))
 }

3 JDBC的资源释放

JDBC程序执行结束后,需要将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection。尤其是Connection对象,一定要做到晚创建,早释放。
如果把所有关闭语句写在同一个try块里面,一旦前面的关闭语句抛异常,后面的关闭语句就无法执行,所以要给每个关闭语句一个try块。
最后为了保证资源能够释放,还有在每个关闭语句的后面加一个finally,在finally里给要关闭的资源赋值为空。这样即使关闭过程中抛异常不能及时关闭,但是由于赋值为空,没有引用该资源,在垃圾回收的时候也能够回收。

 if(rs !=null){    try {
         rs.close();
     } catch (SQLException e) {
         e.printStackTrace();
     }finally{
         rs = null;
     }
 }

 if(statement !=null){
     try {
         statement.close();
     } catch (SQLException e) {
         e.printStackTrace();
     }finally{
         statement = null;
     }
 }

 if(conn !=null){
     try {
         conn.close();
     } catch (SQLException e) {
         e.printStackTrace();
     }finally{
         conn = null;
     }
 }

2.2 JDBC的增删改查

public void demo1(){
    Connection conn = null;
    Statement stmt = null;
    try{
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql:///web_test3", "root", "abc");
        stmt = conn.createStatement();

        //增加一条数据
        String sql = "insert into user values (null,'eee','123','阿黄',21)";
        int num = stmt.executeUpdate(sql);
        if(num > 0){
            System.out.println("保存用户成功!!!");
        }
        /*
        //删除、修改类似

        //查询一条或多条多条
        String sql = "select * from user";
        rs = stmt.executeQuery(sql);
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
        }

        */
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        // 资源释放:略
    }
}

3 JDBC的工具类的抽取

public class JDBCUtils {
    private static final String driverClassName;
    private static final String url;
    private static final String username;
    private static final String password;

    // 获取属性文件中的内容:
    static{
        driverClassName="com.mysql.jdbc.Driver";
        url="jdbc:mysql:///web_test3";
        username="root";
        password="abc";
    }
    /*
    static{
        // 获取属性文件中的内容:
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("src/db.properties"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        driverClassName=properties.getProperty("driverClassName");
        url=properties.getProperty("url");
        username=properties.getProperty("username");
        password=properties.getProperty("password");
    }
    */

    /**
     * 注册驱动的方法
     */
    public static void loadDriver(){
        try {
            Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获得连接的方法
     */
    public static Connection getConnection(){
        Connection conn = null;
        try{
            // 将驱动一并注册:
            loadDriver();
            // 获得连接
            conn = DriverManager.getConnection(url,username, password);
        }catch(Exception e){
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 释放资源的方法
     */
    public static void release(Statement stmt,Connection conn){
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            stmt = null;
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }

    public static void release(ResultSet rs,Statement stmt,Connection conn){
        // 资源释放:
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            rs = null;
        }
        if(stmt != null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            stmt = null;
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
}

测试工具类:

public void demo1(){
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try{
        conn = JDBCUtils.getConnection();
        stmt = conn.createStatement();
        String sql = "select * from user";
        rs = stmt.executeQuery(sql);
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(rs, stmt, conn);
    }
}

4  用 properties 文件配置JDBC信息

1 开发中获得连接的4个参数(驱动、URL、用户名、密码)通常都存在配置文件中,方便后期维护,程序如果需要更换数据库,只需要修改配置文件即可。
使用properties文件要求:
  1.文件位置:任意,建议src下
  2.文件内容:一行一组数据,格式是“key=value”
    a)key命名自定义,如果是多个单词,习惯使用点分隔。例如:jdbc.driver
    b)value值不支持中文,如果需要使用非英文字符,将进行unicode转换

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root

2 加载properties配置文件

public class JDBCUtils {

    private static String driver;
    private static String url;
    private static String user;
    private static String password;
    // 静态代码块
    static {
        try {
            // 1 使用Properties处理流
            // 使用load()方法加载指定的流
            Properties props = new Properties();
            Reader is = new FileReader("db.properties");
            props.load(is);
            // 2 使用getProperty(key),通过key获得需要的值,
            driver = props.getProperty("driver");
            url = props.getProperty("url");
            user = props.getProperty("user");
            password = props.getProperty("password");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获得连接
     */
    public static Connection getConnection() {
        try {
            // 1 注册驱动
            Class.forName(driver);
            // 2 获得连接
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

5 SQL 注入

输入一个存在的用户名 userName,再输入任意密码:XXX' OR 'a' = 'a'时,
SELECT * FROM Table WHERE Name = 'userName' AND PassWord = 'XXX' OR 'a' = 'a';
此时绕过了密码校验成功登录,这便是SQL注入问题。

为此,可以使用PreparedStatement将SQL预先进行编译,使用?作为占位符。当传入变量(包含SQL的关键字)时,不会识别 or 这些关键字。

public class UserDao {

    public boolean login(String username,String password){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        // 定义一个变量:
        boolean flag = false;
        try{
            conn = JDBCUtils.getConnection();
            String sql = "select * from user where username = ? and password = ?";
            // 预编译SQL
            pstmt = conn.prepareStatement(sql);
            // 设置参数:
            pstmt.setString(1, username);
            pstmt.setString(2, password);
            rs = pstmt.executeQuery();
            if(rs.next()){
                flag = true;
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            JDBCUtils.release(rs, pstmt, conn);
        }
        return flag;
    }

6 JDBC批处理

Statement接口:void addBatch(String sql)
PreparedStatement接口重写了addBatch()方法:void addBatch()

public void demo1(){
    Connection conn = null;
    Statement stmt = null;
    try{
        // 获得连接:
        conn = JDBCUtils.getConnection();
        // 创建执行批处理对象:
        stmt = conn.createStatement();
        // 编写一批SQL语句:
        String sql1 = "create database test1";
        String sql2 = "use test1";
        String sql3 = "create table user(id int primary key auto_increment,name varchar(20))";
        String sql4 = "insert into user values (null,'aaa')";
        String sql5 = "insert into user values (null,'bbb')";
        String sql6 = "insert into user values (null,'ccc')";
        String sql7 = "update user set name = 'mmm' where id = 2";
        String sql8 = "delete from user where id = 1";
        // 添加到批处理
        stmt.addBatch(sql1);
        stmt.addBatch(sql2);
        stmt.addBatch(sql3);
        stmt.addBatch(sql4);
        stmt.addBatch(sql5);
        stmt.addBatch(sql6);
        stmt.addBatch(sql7);
        stmt.addBatch(sql8);
        // 执行批处理:
        stmt.executeBatch();
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(stmt, conn);
    }
}

//批量插入(使用PreparedStatement)
    @Test
    /**
     * 批量插入记录:
     * * 默认情况下MySQL批处理没有开启的,需要在url后面拼接一个参数即可。
     */
    public void demo2(){
        // 记录开始时间:
        long begin = System.currentTimeMillis();
        Connection conn = null;
        PreparedStatement pstmt = null;
        try{
            conn = JDBCUtils.getConnection();
            String sql = "insert into user values (null,?)";
            pstmt = conn.prepareStatement(sql);
            for(int i=1;i<=10000;i++){
                pstmt.setString(1, "name"+i);
                pstmt.addBatch();
                if(i % 1000 == 0){
                    pstmt.executeBatch();
                    pstmt.clearBatch();
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            JDBCUtils.release(pstmt, conn);
        }
        long end = System.currentTimeMillis();
        System.out.println((end-begin));
    }

7 JDBC事务管理

例如在转账操作中,如果没有添加事务管理,可能出现 a b 账户互相转账后,两账户总额跟操作前不相等的情况,此时需要给转账功能添加事务管理。

public void demo1(){
        Connection conn = null;
        PreparedStatement pstmt  = null;
        try{
            conn = JDBCUtils.getConnection();
            // 1 开启事务
            conn.setAutoCommit(false);
            String sql = "update account set money = money + ? where name = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setDouble(1, -1000);
            pstmt.setString(2, "aaa");
            pstmt.executeUpdate();

            int i = 1 / 0;

            pstmt.setDouble(1, 1000);
            pstmt.setString(2, "bbb");
            pstmt.executeUpdate();

            // 2 提交事务:
            conn.commit();
        }catch(Exception e){
            // 3 回滚事务:
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally{
            JDBCUtils.release(pstmt, conn);
        }
    }

8 DBUtils简介

为简化JDBC开发,可以使用DBUtils,DBUtils封装了对JDBC的操作,是JDBC的简化开发工具包。使用需要导入commons-dbutils-1.6.jar。
Dbutils三个核心功能
    QueryRunner      提供对sql语句操作的API,用来执行SQL语句的对象
    ResultSetHandler接口   定义select后怎样封装结果集.
    DbUtils类        工具类,定义了关闭资源与事务处理的方法
1 QueryRunner
    无事务管理:
      int update(String sql,Object… args);:增、删、改
      T query(String sql,ResultSetHandler rsh,Object… args);:查询
    有事务管理:
      int update(Connection conn, String sql, Object... params) :增、删、改           
      T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) :查询
query(Connection con,String sql,ResultSetHandler r, Object..params)
  ResultSetHandler r   结果集的处理方式,传递ResultSetHandler接口实现类
  Object..params     SQL语句中的?占位符
  注意: query方法返回值,返回的是T 泛型, 具体返回值类型,跟随结果集处理方式变化

//增加
public void demo1() throws SQLException{
    // 创建核心类:QueryRunner:
    QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    queryRunner.update("insert into account values (null,?,?)", "ddd",10000);
}

//删除
public void demo3() throws SQLException{
    QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    queryRunner.update("delete from account where id = ?", 3);
}
//修改
public void demo2() throws SQLException{
    QueryRunner queryRunner = new QueryRunner(JDBCUtils2.getDataSource());
    queryRunner.update("update account set name=?,money=? where id =?", "eee",20000,4);
}  
//增加
public void insert(){
    try {
        //获取一个QueryRunner对象,用来执行SQL语句
        QueryRunner queryRunner = new QueryRunner();

        String sql = "INSERT INTO zhangwu(name,money,parent) VALUES(?,?,?)";
        Object[] params = {"股票收入", 5500, "收入"};
        Connection conn = JDBCUtils.getConnection();
        int line = queryRunner.update(conn,sql,params);// 用来完成表数据的增加、删除、更新操作
        //结果集处理
        System.out.println("line = " + line);

    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}
//删除
public void delete(){
    try {
        QueryRunner queryRunner = new QueryRunner();
        String sql = "DELETE FROM zhangwu WHERE name = ?";
        Object[] params = {"股票收入"};
        Connection conn = JDBCUtils.getConnection();
        int line = queryRunner.update(conn, sql, params);
        System.out.println("line="+line);

    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}
//修改
public void update(){
    try {
        QueryRunner queryRunner = new QueryRunner();
        String sql = "UPDATE zhangwu SET money = money+1000 WHERE name=?";
        Object[] params = {"股票收入"};
        Connection conn = JDBCUtils.getConnection();
        int line = queryRunner.update(conn, sql, params);
        System.out.println("line="+line);

    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}

QueryRunner的两个构造方法
第一种:不带connection参数
QueryRunner queryRunner = new QueryRunner();
这种情况下,调用update或query方法时,需要传入对应的connection参数
queryRunner.update(conn, sql,params);
conn.close();
DBUtils调用这种带connection参数的方法时,
只会关闭preparedstatement和resultset对象,不会关闭conneciton对象,
一些情况下,没有手动关闭,可能会导致连接池满了,访问数据库是处于一直等待的状态。
就是为了其他方法来调用这个conneciton,所以这种连接数据库的方法适合操作事务。
第二种:带connection参数
QueryRunner queryRunner = new QueryRunner(dataSource);
将dataSource传递进去,这样update或query方法内部就调用this.getconnection方法来从这个数据源获得连接,
queryRunner.update( sql,params);
操作完后,就关闭conneciton,preparedstatement和resultset对象.
事务是自动控制的,一条SQL语句一个事务,不需要人为的控制。
2 ResultSetHandler  

  ArrayHandler        将结果集的第一条记录封装到Object[]数组中,数组中的每一个元素就是这条记录中每一个字段的值
  ArrayListHandler     将结果集的每一条记录都封装到Object[]数组中,数组封装到List集合中。
  BeanHandler        将结果集第一条记录封装到javaBean中。
  BeanListHandler     将结果集每一条记录封装到javaBean中,javaBean封装到List集合中
  ColumnListHandler    将结果集指定的列的字段值,封装到一个List集合中
  ScalarHandler      用于单数据。例如select count(*) from 表操作。
  MapHandler       将结果集第一行封装到Map集合中,Key 列名, Value 该列数据
  MapListHandler       将结果集第一行封装到Map集合中,Key 列名, Value 该列数据,Map集合存储到List集合

  1)JavaBean
JavaBean就是一个类,在开发中常用封装数据。具有如下特性
1.需要实现接口:java.io.Serializable ,通常实现接口这步骤省略了,不会影响程序。
2.提供私有字段:private 类型 字段名;
3.提供getter/setter方法:
4.提供无参构造
示例:

public class ZhangWu {
    private int id;
    private String name;
    private double money;
    private String parent;

    public ZhangWu() {
        super();
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    public String getParent() {
        return parent;
    }
    public void setParent(String parent) {
        this.parent = parent;
    }

    @Override
    public String toString() { //该方法可以省略
        return "ZhangWu [id=" + id + ", name=" + name + ", money=" + money + ", parent=" + parent + "]";
    }
}

3 ResultSetHandler使用案例

public class ArrayHandlerDemo {

    public void method(){
        try {
            //获取QueryRunner对象
            QueryRunner qr = new QueryRunner();
            //执行SQL语句
            String sql = "SELECT * FROM zhangwu";
            Object[] params = {};
            Connection conn = JDBCUtils.getConnection();
            Object[] objArray = qr.query(conn, sql, new ArrayHandler(), params);
            //结果集的处理
            System.out.println( Arrays.toString(objArray) );

            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

//ArrayListHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<Object[]> list = qr.query(conn, sql, new ArrayListHandler(), params);
for (Object[] objArray : list) {
    System.out.println(  Arrays.toString(objArray) );
}

//BeanHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE id=?";
Object[] params = {1};
Connection conn = JDBCUtils.getConnection();
ZhangWu zw = qr.query(conn, sql, new BeanHandler<ZhangWu>(ZhangWu.class), params);
System.out.println(zw);

//BeanListHandlerDemo
String sql = "SELECT * FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<ZhangWu> list = qr.query(conn, sql, new BeanListHandler<ZhangWu>(ZhangWu.class), params);
for (ZhangWu zw : list) {
    System.out.println(zw);
}

//ColumnListHandlerDemo
String sql = "SELECT name FROM zhangwu WHERE money>?";
Object[] params = {2000};
Connection conn = JDBCUtils.getConnection();
List<String> list = qr.query(conn, sql, new ColumnListHandler<String>(), params);
for (String str : list) {
    System.out.println(str);
}

//ScalarHandlerDemo
String sql = "SELECT MAX(money) FROM zhangwu";
Object[] params = {};
Connection conn = JDBCUtils.getConnection();
Double max = qr.query(conn, sql, new ScalarHandler<Double>(), params);
System.out.println("max=" + max);
/*
 *  结果集第一种处理方法, ArrayHandler
 *  将结果集的第一行存储到对象数组中  Object[]
 */
public static void arrayHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT * FROM sort";
    //调用方法query执行查询,传递连接对象,SQL语句,结果集处理方式的实现类
    //返回对象数组
    Object[] result = qr.query(con, sql, new ArrayHandler());
    for(Object obj : result){
        System.out.print(obj);
    }
}    

/*
 *  结果集第二种处理方法,ArrayListHandler
 *  将结果集的每一行,封装到对象数组中, 出现很多对象数组
 *  对象数组存储到List集合
 */
public static void arrayListHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT * FROM sort";
    //调用query方法,结果集处理的参数上,传递实现类ArrayListHandler
    //方法返回值 每行是一个对象数组,存储到List
    List<Object[]> result=  qr.query(con, sql, new ArrayListHandler());

    //集合的遍历
    for( Object[] objs  : result){
        //遍历对象数组
        for(Object obj : objs){
            System.out.print(obj+"  ");
        }
        System.out.println();
    }
}

/*
 *  结果集第三种处理方法,BeanHandler
 *  将结果集的第一行数据,封装成JavaBean对象
 *  注意: 被封装成数据到JavaBean对象, Sort类必须有空参数构造
 */
public static void beanHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT * FROM sort ";
    //调用方法,传递结果集实现类BeanHandler
    //BeanHandler(Class<T> type)
    Sort s = qr.query(con, sql, new BeanHandler<Sort>(Sort.class));
    System.out.println(s);
}

/*
*  结果集第四种处理方法, BeanListHandler
*  结果集每一行数据,封装JavaBean对象
*  多个JavaBean对象,存储到List集合
*/
public static void beanListHander()throws SQLException{
QueryRunner qr = new QueryRunner();
String sql = "SELECT * FROM sort ";
//调用方法query,传递结果集处理实现类BeanListHandler
List<Sort> list = qr.query(con, sql, new BeanListHandler<Sort>(Sort.class));
for(Sort s : list){
    System.out.println(s);
}
}

/*
 *  结果集第五种处理方法,ColumnListHandler
 *  结果集,指定列的数据,存储到List集合
 *  List<Object> 每个列数据类型不同
 */
public static void columnListHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT * FROM sort ";
    //调用方法 query,传递结果集实现类ColumnListHandler
    //实现类构造方法中,使用字符串的列名
    List<Object> list = qr.query(con, sql, new ColumnListHandler<Object>("sname"));
    for(Object obj : list){
        System.out.println(obj);
    }
}    

/*
 *  结果集第六种处理方法,ScalarHandler
 *  对于查询后,只有1个结果
 */
public static void scalarHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT COUNT(*) FROM sort";
    //调用方法query,传递结果集处理实现类ScalarHandler
    long count = qr.query(con, sql, new ScalarHandler<Long>());
    System.out.println(count);
}

/*
 *  结果集第七种处理方法,MapHandler
 *  将结果集第一行数据,封装到Map集合中
 *  Map<键,值> 键:列名  值:这列的数据
 */
public static void mapHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT  * FROM sort";
    //调用方法query,传递结果集实现类MapHandler
    //返回值: Map集合,Map接口实现类, 泛型
    Map<String,Object> map = qr.query(con, sql, new MapHandler());
    //遍历Map集合
    for(String key : map.keySet()){
        System.out.println(key+".."+map.get(key));
    }
}

/*
 *  结果集第八种处理方法,MapListHandler
 *  将结果集每一行存储到Map集合,键:列名,值:数据
 *  Map集合过多,存储到List集合
 */
public static void mapListHandler()throws SQLException{
    QueryRunner qr = new QueryRunner();
    String sql = "SELECT  * FROM sort";
    //调用方法query,传递结果集实现类MapListHandler
    //返回值List集合, 存储的是Map集合
    List<Map<String,Object>> list = qr.query(con, sql, new MapListHandler());
    //遍历集合list
    for( Map<String,Object> map : list ){
        for(String key : map.keySet()){
            System.out.print(key+"..."+map.get(key));
        }
        System.out.println();
    }

}

9 连接池

9.1 DBCP连接池

Java提供了数据库连接池公共接口:javax.sql.DataSource,各厂商需要让自己的连接池实现这个接口,这样程序可以方便地切换不同厂商的连接池。

DBCP是tomcat内置开源连接池,提供了DataSource接口的实现类:BasicDataSource类
需要的jar包:
mysql-connector-java-5.1.37-bin.jar:数据库驱动
commons-dbutils-1.6.jar:提供QueryRunner类方便进行增删改查操作
commons-dbcp-1.4.jar:
commons-pool-1.5.6.jar:提供高效的数据库连接池技术
BasicDataSource类的使用:

//使用DBCP实现数据库的连接池
public class JDBCUtils{
    //创建出BasicDataSource类对象
    private static BasicDataSource datasource = new BasicDataSource();

    static{
        datasource.setDriverClassName("com.mysql.jdbc.Driver");
        datasource.setUrl("jdbc:mysql://localhost:3306/day33_user");
        datasource.setUsername("root");
        datasource.setPassword("123");
        datasource.setInitialSize(10);//初始化的连接数
        datasource.setMaxActive(8);//最大连接数量
        datasource.setMaxIdle(5);//最大空闲数
        datasource.setMinIdle(1);//最小空闲
    }

    //定义静态方法,返回BasicDataSource类的对象
    public static DataSource getDataSource(){
        return datasource;
    }
}

常见配置
必须项
  driverClassName   数据库驱动名称
  url         数据库的地址
  username      用户名
  password      密码
基本项(扩展)
  maxActive   最大连接数量
  minIdle    最小空闲连接
  maxIdle    最大空闲连接
  initialSize   初始化连接
测试连接池:

/*
 *  测试写好的工具类,
 *  提供的是一个DataSource接口的数据源
 *  QueryRunner类构造方法,接收DataSource接口的实现类
 *  后面,调用方法update,query,无需传递他们Connection连接对象
 */

public class QueryRunnerDemo{
    public static void main(String[] args) {
        select();
    }
    //定义2个方法,实现数据表的添加,数据表查询
    //QueryRunner类对象,写在类成员位置
    private static QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource()); 

    //数据表查询
    public static void select(){
        String sql = "SELECT * FROM sort";
        try{
        List<Object[]> list = qr.query(sql, new ArrayListHandler());
        for(Object[] objs : list){
            for(Object obj : objs){
                System.out.print(obj+"\t");
            }
            System.out.println();
        }
        }catch(SQLException ex){
            throw new RuntimeException("数据查询失败");
        }
    }

}

9.2 C3P0连接池

C3P0是一个开源JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
C3P0与DBCP区别:DBCP没有自动回收空闲连接的功能,C3P0有
C3P0连接池的使用:

/**
 * 1 手动设置参数的方式:
 */
public void demo1(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try{
        // 获得连接:从连接池中获取:
        // 创建连接池:
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        // 设置连接参数:
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///web_test4");
        dataSource.setUser("root");
        dataSource.setPassword("abc");

        conn = dataSource.getConnection();
        String sql = "select * from account";
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(rs, pstmt, conn);
    }
}
/**
 * 2 采用配置文件的方式:
 */
public void demo2(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try{
        // 获得连接:从连接池中获取:
        // 创建连接池://创建连接池默认去类路径下查找c3p0-config.xml
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        // 从连接池中获得连接:
        conn = dataSource.getConnection();
        String sql = "select * from account";
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(rs, pstmt, conn);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- c3p0-config.xml -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql:///web_test4</property>
        <property name="user">root</property>
        <property name="password">abc</property>

        <property name="initialPoolSize">5</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config> 

</c3p0-config>

C3P0详细配置:

<?xml version="1.0" encoding="gbk"?>
<c3p0-config>
<!--默认配置,如果获取连接池时没有指定名称,则使用默认配置信息-->
<default-config>
    <!--连接url-->
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>

    <!--数据库驱动类-->
    <property name="driverClass">com.mysql.jdbc.Driver</property>

    <!--用户名。Default: null-->
    <property name="user">root</property>
    <!--密码。Default: null-->
    <property name="password"></property>

    <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
    <property name="acquireIncrement">3</property>

    <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
     <property name="acquireRetryAttempts">30</property>

    <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->
     <property name="acquireRetryDelay">1000</property>

    <!--连接关闭时默认将所有未提交的操作回滚。Default: false -->
     <property name="autoCommitOnClose">false</property>

    <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么
     属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试
     使用。Default: null-->
    <property name="automaticTestTable">Test</property>

    <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
     保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
     获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->
    <property name="breakAfterAcquireFailure">false</property>

    <!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出
    SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->
    <property name="checkoutTimeout">100</property>

    <!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。
    Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
    <property name="connectionTesterClassName"></property>

    <!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可
    Default: null-->
    <property name="factoryClassLocation">null</property>

    <!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
    (文档原文)作者强烈建议不使用的一个属性-->
    <property name="forceIgnoreUnresolvedTransactions">false</property>

    <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
    <property name="idleConnectionTestPeriod">60</property>

    <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
    <property name="initialPoolSize">3</property>

    <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
    <property name="maxIdleTime">60</property>

    <!--连接池中保留的最大连接数。Default: 15 -->
    <property name="maxPoolSize">15</property>

    <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
    属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
     如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->
    <property name="maxStatements">100</property>

    <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
    <property name="maxStatementsPerConnection"></property>

    <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能
     通过多线程实现多个操作同时被执行。Default: 3-->
     <property name="numHelperThreads">3</property>

    <!--当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0
    的数据源时。Default: null-->
    <property name="overrideDefaultUser">root</property>

    <!--与overrideDefaultUser参数对应使用的一个参数。Default: null-->
    <property name="overrideDefaultPassword">password</property>

    <!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:
     测试的表必须在初始数据源的时候就存在。Default: null-->
     <property name="preferredTestQuery">select id from test where id=1</property>

    <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->
    <property name="propertyCycle">300</property>

    <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
     时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
    等方法来提升连接测试的性能。Default: false -->
    <property name="testConnectionOnCheckout">false</property>

    <!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->
    <property name="testConnectionOnCheckin">true</property>

    <!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数
     允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始
     广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到
     支持,但今后可能的版本可能不支持动态反射代理。Default: false-->
    <property name="usesTraditionalReflectiveProxies">false</property>

 </default-config>
    <!--指定配置名称的配置信息-->
    <named-config name="dumbTestConfig">

    </named-config>
 </c3p0-config>

9.3 Druid连接池

阿里旗下开源连接池产品,使用简单,可以与Spring框架进行快速整合
1 Druid的使用:

/**
 * 1 手动设置参数的方式
 */
public void demo1(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try{
        // 使用连接池:
        DruidDataSource dataSource = new DruidDataSource();
        // 手动设置数据库连接的参数:
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///web_test4");
        dataSource.setUsername("root");
        dataSource.setPassword("abc");
        // 获得连接:旧方式
        //conn = JDBCUtils.getConnection();
        conn = dataSource.getConnection();

        String sql = "select * from account";
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(rs, pstmt, conn);
    }
}
/**
 * 2 配置方式设置参数
 * Druid配置方式可以使用属性文件配置的。
 * 文件名称没有规定但是属性文件中的key要一定的。
 */
public void demo2(){
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try{
        // 使用连接池:
        // 从属性文件中获取:
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/druid.properties"));
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        // 获得连接:旧方式
        //conn = JDBCUtils.getConnection();
        conn = dataSource.getConnection();

        String sql = "select * from account";
        pstmt = conn.prepareStatement(sql);
        rs = pstmt.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        JDBCUtils.release(rs, pstmt, conn);
    }
}

2 Druid监控及慢sql记录:
https://www.cnblogs.com/telwanggs/p/7484854.html

9.4 自定义连接池

步骤:
  1 编写一个类实现DataSource接口
  2 重写getConnection方法
  3 初始化多个连接在内存中
  4 编写归还连接的方法

//自定义连接池
public class MyDataSource implements DataSource {
    // 将一些连接存入到内存中,可以定义一个集合,用于存储连接对象。
    private List<Connection> connList = new ArrayList<Connection>();

    // 在初始化的时候提供一些连接
    public MyDataSource() {
        // 初始化连接:
        for(int i = 1;i<=3;i++){
            // 向集合中存入连接:
            connList.add(JDBCUtils.getConnection());
        }
    }

    // 从连接池中获得连接的方法
    @Override
    public Connection getConnection() throws SQLException {
        Connection conn = connList.remove(0);
        // 增强连接:
        MyConnectionWrapper connWrapper = new MyConnectionWrapper(conn, connList);
        return connWrapper;
    }

    // 编写一个归还连接的方法:
    /*public void addBack(Connection conn){
        connList.add(conn);
    }*/

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter arg0) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int arg0) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean isWrapperFor(Class<?> arg0) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Connection getConnection(String arg0, String arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}

测试自定义连接池:

public class DataSourceDemo1 {

    @Test
    /**
     * 测试自定义连接池
     */
    public void demo1(){
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        DataSource dataSource = null;
        try{
            // 获得连接:
            // conn = JDBCUtils.getConnection();
            // 从连接池中获得连接:
            dataSource = new MyDataSource();
            conn = dataSource.getConnection();
            String sql = "select * from account";
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            while(rs.next()){
                System.out.println(rs.getInt("id")+" "+rs.getString("name")+" "+rs.getDouble("money"));
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            JDBCUtils.release(rs, pstmt, conn);
            // 归还连接:
            // dataSource.addBack(conn);
        }
    }
}

使用装饰者增强Connection中的close方法:将原有的close方法的逻辑改为归还(增强一个类中的方法) 
装饰者模式使用条件:
  1、增强类和被增强类实现相同的接口
  2、在增强的类中获得被增强的类的引用

public class MyConnectionWrapper extends ConnectionWrapper{

    private Connection conn;
    private List<Connection> connList;

    public MyConnectionWrapper(Connection conn,List<Connection> connList) {
        super(conn);
        this.conn = conn;
        this.connList= connList;
    }

    // 增强某个方法:
    @Override
    public void close() throws SQLException {
//        super.close();
        // 归还连接:
        connList.add(conn);
    }

}

JDBC 和连接池的更多相关文章

  1. c3p0、dbcp、tomcat jdbc pool 连接池配置简介及常用数据库的driverClass和驱动包

    [-] DBCP连接池配置 dbcp jar包 c3p0连接池配置 c3p0 jar包 jdbc-pool连接池配置 jdbc-pool jar包 常用数据库的driverClass和jdbcUrl ...

  2. jdbc数据连接池dbcp要导入的jar包

    jdbc数据连接池dbcp要导入的jar包 只用导入commons-dbcp-x.y.z.jarcommons-pool-a.b.jar

  3. 关于JDBC和连接池我学到的(转载保存)

    1.JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模 ...

  4. JDBC之 连接池

    JDBC之 连接池 有这样的一种现象: 用java代码操作数据库,需要数据库连接对象,一个用户至少要用到一个连接.现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力 ...

  5. JDBC数据源连接池(4)---自定义数据源连接池

    [续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...

  6. JDBC数据源连接池(3)---Tomcat集成DBCP

    此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...

  7. JDBC数据源连接池(2)---C3P0

    我们接着<JDBC数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3P0的j ...

  8. DBCP,C3P0与Tomcat jdbc pool 连接池的比较

    hibernate开发组推荐使用c3p0; spring开发组推荐使用dbcp(dbcp连接池有weblogic连接池同样的问题,就是强行关闭连接或数据库重启后,无法reconnect,告诉连接被重置 ...

  9. JDBC数据源连接池的配置和使用实例

    个人学习参考所用,勿喷! 使用JDBC建立数据库连接的两种方式: 1.在代码中使用DriverManager获得数据库连接.这种方式效率低,并且其性能.可靠性和稳定性随着用户访问量得增加逐渐下降. 2 ...

  10. mysql,jdbc、连接池

    show processlist; select * from information_schema.processlist; Command: The type of command the thr ...

随机推荐

  1. MyBatis3 入门学习指南

    官网原文:http://www.mybatis.org/mybatis-3/zh/index.html 1.简介 1.1 什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持定制化 ...

  2. TensorFlow与caffe中卷积层feature map大小计算

    刚刚接触Tensorflow,由于是做图像处理,因此接触比较多的还是卷及神经网络,其中会涉及到在经过卷积层或者pooling层之后,图像Feature map的大小计算,之前一直以为是与caffe相同 ...

  3. TCP 协议简析

    TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的.可靠的.基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接.它是个超级麻烦的协议, ...

  4. SQL Server 2016新特性:列存储索引新特性

    SQL Server 2016新特性:列存储索引新特性 行存储表可以有一个可更新的列存储索引,之前非聚集的列存储索引是只读的. 非聚集的列存储索引支持筛选条件. 在内存优化表中可以有一个列存储索引,可 ...

  5. 凭什么相信你,我的CNN模型

    背景 学术界一直困惑的点是"如何让看似黑盒的CNN模型说话",即对它的分类结果给出解释. 这里的解释是指,让模型告诉我们它是通过图片的哪些像素做出判断的,并不是深度学习理论层面的解 ...

  6. 微信小程序——购物车结算

    项目需要做个购物车结算功能,先分析需求: 1.全选,反选的功能.当选中的个数 = 购物车的数量时,勾选全选按钮,反之则取消选中全选按钮: 2.改变选中状态时,计算总价和总数量: 3.单个产品的数量加减 ...

  7. ifconfig 中的 eth0 eth0:1 eth0.1 与 lo

    1. eth0 eth0:1 eth0.1 eth0 eth0:1 和eth0.1三者的关系对应于物理网卡.子网卡.虚拟VLAN网卡的关系:物理网卡:物理网卡这里指的是服务器上实际的网络接口设备,这里 ...

  8. js 零散知识总结

    网页播放声音 这个非常简单,我们只需要在html和js设置即可.首先看html代码 html代码 <audio id="sound" autoplay="autop ...

  9. 21备忘录模式Memento

    一.什么是备忘录模式 Memento模式也叫备忘录模式,是行为模式之 一,它的作用是保存对象的内部状态,并在需要 的时候(undo/rollback)恢复对象以前的状态. 二.备忘录模式的应用场景 如 ...

  10. Mysql Window 解压版卸载

    windows如何彻底卸载mysql 如何彻底删除mysql 1.首先在windows服务中将mysql服务删掉,使用命令 sc delete mysql 2.在控制面板中卸载掉mysql. 3.清理 ...