javaWeb基础一:JDBC (java操作数据库的技术)

1. JDBC

1.1 含义

JDBC : java database connectivity

由SUN公司提供的一套操作数据库的标准规范

JDBC与数据库驱动的关系:接口与实现的关系。

1.2 四大类

  1. DriverManager:用于注册驱动
  2. Connection: 表示与数据库创建的连接
  3. Statement: 操作数据库sql语句的对象
  4. ResultSet: sql结果集

1.3 操作步骤

1.3.1 加载驱动

操作数据库之前,需要先将对应数据库的驱动包导入项目,注:驱动是由对应数据库厂商实现的。

加载驱动:

//1.导入驱动包
Class.forName("com.mysql.jdbc.Driver");

1.3.2 创建连接对象

创建连接对象需要获取url 、username 、password

示例:

//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "1234";
Connection connection = DriverManager.getConnection(url, username, password);

注:test为数据库名、username为数据库用户名、password为数据库用户密码。

1.3.3 创建Statement对象

示例:

//3.获取Statement的对象(目的:向MySQL发送SQL指令)
Statement statement = connection.createStatement();

1.3.4 通过Statement对象执行sql语句

示例:

//4.利用Statement对象向MySQL发送SQL指令

//sql语句
String sql = "INSERT INTO student(name,sex,age,salary,course,t_id) VALUES('小花','女',18,12000,'Java',1);";
//执行SQL语句(返回的num为受影响的行数)
int num = statement.executeUpdate(sql);
System.out.println("受影响的行数:" + num);

1.3.5 获取执行结果

获取执行结果:通过ResultSet获取SQL返回的结果集

注意:

在进行数据库的插入、修改、删除操作时都使用 statement.executeUpdate(sql);方法,而此方法返回值为受影响的行数(int)。

查询操作(select)需要通过statement.executeQuery(sql);方法,此方法返回一个ResultSet对象,此对象中存储有从数据库查询得到的数据。

示例:(查询数据库数据)

//利用JDBC查询数据

//1.导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "1234";
Connection connection = DriverManager.getConnection(url, username, password);
//3.获取Statement的对象(目的:向MySQL发送SQL指令)
Statement statement = connection.createStatement();
//4.利用Statement对象向MySQL发送SQL指令
String sql = "SELECT * FROM  student;";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
    //通过字段名获取对应的数据
    int id = resultSet.getInt("id");
    String name = resultSet.getString("name");
    String sex = resultSet.getString("sex");
    int age = resultSet.getInt("age");
    BigDecimal salary = resultSet.getBigDecimal("salary");
    String course = resultSet.getString("course");
    int t_id = resultSet.getInt("t_id");
    System.out.println(id+"-"+name+"-"+sex+"-"+age+"-"+salary+"-"+course+"-"+t_id);
}

//5.关闭资源
resultSet.close();
statement.close();
connection.close();

1.3.6 资源的关闭

注:资源关闭的顺序和资源开启的顺序相反,即:

//5.关闭资源
resultSet.close();
statement.close();
connection.close();

1.4 利用JDBC增删查改

示例:(查询数据的示例见上)

//利用JDBC增、删、改 数据

//1.导入驱动包
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "1234";
Connection connection = DriverManager.getConnection(url, username, password);
//3.获取Statement的对象(目的:向MySQL发送SQL指令)
Statement statement = connection.createStatement();
//4.利用Statement对象向MySQL发送SQL指令

//插入
String sql1 = "INSERT INTO student(name,sex,age,salary,course,t_id) VALUES('小花','女',18,12000,'Java',1);"
//修改
String sql2 = "UPDATE student SET age=60 WHERE id=1;";        
//删除
String sql3 = "DELETE FROM  student  WHERE id=3;";


int num1 = statement.executeUpdate(sql1);
int num2 = statement.executeUpdate(sql2);
int num3 = statement.executeUpdate(sql3);
System.out.println("受影响的行数:" + num1+"---"+num2+"---"+num3);
//5.关闭资源
statement.close();
connection.close();

1.5 sql注入问题和预编译

在使用Statement处理SQL语句时,有可能会发生SQL语句注入问题,即如下示例:

String sql = "select * from student where name='' or 1=1 #' and passwd='111111';";
ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()){
    System.out.println("登录成功!");
}else
    System.out.println("登录失败!");

/*
登录成功!
*/

原因:直接拼接成的SQL语句提交给数据库时,按照数据库SQL语法执行,or 1=1为真,其后的#代表注释,所以其后的语句不会执行。

处理方法:使用预处理(PreparedStatement)示例:

preparedStatement = connection.prepareStatement("UPDATE bank SET money=money+? WHERE id=?;");
preparedStatement.setInt(1,money);
preparedStatement.setInt(2,id);

preparedStatement.executeUpdate();

使用?代替值,表示预处理,再通过set方法给?赋值。

注:?位置下标从1开始。PreparedStatement为Statement的子类

1.6 批处理

在jdbc的url中添加rewriteBatchedStatements=true参数,可以提高批处理执行效率。

在我们进行大批量数据操作的时候,需要采用批处理的方式来提高程序的运行性能,目的是减少跟数据库交互的次数。

使用Statement 的addBatch();和executeBatch();方法

1.6.1 批处理多条不同的SQL语句

示例:

String sql1 = "INSERT INTO student VALUES(9,'小刘','男',21,11000,'Java',1);";
String sql2 = "UPDATE student SET age=18 WHERE id=9;";

Connection connection = DBUtil.getConnection();
Statement statement = connection.createStatement();

//将SQL语句添加到batch包中
statement.addBatch(sql1);
statement.addBatch(sql2);

//提交batch包
statement.executeBatch();

//关闭资源
DBUtil.close(connection,statement,null);

1.6.2 批处理多条相同的sql语句

示例:

/**
 * 批处理:插入100条sql语句
 */
String sql = "INSERT INTO student(name,sex,age,salary,course,t_id) VALUES(?,?,?,?,?,?);";

Connection connection = DBUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);

for (int i=1;i<100;i++){
    statement.setString(1,"王"+i);
    statement.setString(2,"男");
    statement.setInt(3,20);
    statement.setBigDecimal(4, new BigDecimal("12000"));
    statement.setString(5,"java");
    statement.setInt(6,1);

    //将SQL语句添加到batch包中
    statement.addBatch();
}
//提交Batch包
statement.executeBatch();

//关闭资源
DBUtil.close(connection,statement,null);

16.3 处理大量SQL语句(需要批次提交)

示例:

/**
 * 批处理:插入10000条sql语句
 */
String sql = "INSERT INTO student(name,sex,age,salary,course,t_id) VALUES(?,?,?,?,?,?);";

Connection connection = DBUtil.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);

for (int i=1;i<10000;i++){
    statement.setString(1,"巅峰赛"+i);
    statement.setString(2,"男");
    statement.setInt(3,20);
    statement.setBigDecimal(4, new BigDecimal("12000"));
    statement.setString(5,"java");
    statement.setInt(6,1);
    //将SQL语句添加到batch包中
    statement.addBatch();
    if (i%1000==0){
        //提交Batch包
        statement.executeBatch();
        //清空Batch包
        statement.clearBatch();
    }
}
//关闭资源
DBUtil.close(connection,statement,null);

注:示例中使用了DBUtil工具类,详细见1.8工具类

1.7 事务

JDBC控制事务语句:

Connection.setAutoCommit(false);  //关闭自动提交,start transaction

Connection.rollback(); // 事务回滚 rollback

Connection.commit(); // 提交事物  commit

事务的使用:(获取连接对象已封装入DBUtil)

Connection connection =null;
Statement statement =null;
PreparedStatement preparedStatement =null;
try {
    //1.加载驱动,获取连接对象
    connection = DBUtil.getConnection();
    //2.设置为不自动提交
    connection.setAutoCommit(false);

    statement = connection.createStatement();
    preparedStatement = connection.prepareStatement("UPDATE bank SET money=money-? WHERE id=?;");

    preparedStatement.setInt(1,200);
    preparedStatement.setInt(2,1);

    //String sql1="UPDATE bank SET money=money-200 WHERE id=1;";
    String sql2="UPDATE bank SET money=money+200 WHERE id=2;";
    //String sql3="SET money=money+200 WHERE id=2;";

    //statement.executeUpdate(sql1);
    //statement.executeUpdate(sql3);
    preparedStatement.executeUpdate();
    statement.executeUpdate(sql2);

    //3.提交事物
    connection.commit();


} catch (SQLException throwables) {
    System.out.println("sql异常");
    try {
        //事物回滚
        connection.rollback();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}finally {
    DBUtil.close(connection,statement,null);
}
}

1.8 工具类(DBUtil)

通过上面的示例可以发现,无论增删查改,都有相同的步骤和代码,为了避免代码的冗余,提倡在操作数据库时,通过DBUtil工具类进行操作,但要注意以下几点:

  1. 工具类应该要满足多种通用场景。
  2. 满足多线程使用。
  3. 拥有事务处理功能。
  4. 查询到的数据自动封装实体类对象放入集合。
  5. 统一关闭资源。

实现:1. 加载驱动的路径、用户名、密码应该存入配置文件,使用时从配置文件中读取。

​ 2. 同一个线程使用同一个Connection,将Connection存入线程安全的容器ThreadLocal

​ 3. 增加开启事物,提交事务和事务回滚的方法。

​ 4. 实体类不确定,使用传参时应该使用泛型代表。

​ 5. 反射机制的运用。

示例:

package com.dream.test03;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Properties;

public class DBUtil {

    private static ThreadLocal<Connection> local=new ThreadLocal<>();
    private static String url;
    private static String userName;
    private static String passWord;
    private static Properties vo;

    static {
        //加载Sql配置文件
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
        } catch (IOException e) {
            System.out.println("DBUtil:配置文件读取错误!");
        }
        String driverName = properties.getProperty("driverName");
        url = properties.getProperty("url");
        userName = properties.getProperty("userName");
        passWord = properties.getProperty("passWord");
        try {
            //加载驱动
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            System.out.println("DBUtil:驱动加载错误!");
        }
        //加载数据库映射文件
        vo = new Properties();
        try {
            vo.load(DBUtil.class.getClassLoader().getResourceAsStream("VOConfig.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //获取连接对象Connection
    public static Connection getConnection(){
        if (local.get()==null){
            Connection connection = null;
            try {
                connection = DriverManager.getConnection(url,userName,passWord);
            } catch (SQLException e) {
                System.out.println("DBUtil:Connection连接错误!");
            }
            local.set(connection);
        }
        return local.get();
    }

    //开启事务
    public static void startTransaction(){
        try {
            getConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
            System.out.println("DBUtil:事务开启错误!");
        }
    }
    //提交事物
    public static void commit() {
        Connection connection=local.get();
        if (connection!=null){
            try {
                connection.commit();
                connection.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:事务提交错误!");
            }
            local.set(null);
        }
    }
    //回滚事务
    public static void rollback() {
        Connection connection=local.get();
        if (connection!=null){
            try {
                connection.rollback();
                connection.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:回滚事务错误!");
            }
            local.set(null);
        }
    }
    //关闭资源
    public static void close(Connection connection,Statement statement,ResultSet resultSet) {
        if (resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:resultSet关闭错误!");
            }
        }
        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:statement关闭错误!");
            }
        }
        if (connection!=null){
            try {
                if (connection.getAutoCommit()){
                    try {
                        connection.close();
                        local.set(null);
                    } catch (SQLException throwables) {
                        System.out.println("DBUtil:connection关闭错误!");
                    }
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    //主键回填
    /**
     * 插入并返回主键
     * @param sql   SQL命令
     * @param pram  参数
     * @return 主键
     */
    public static int commInsert(String sql,Object... pram){
        Connection connection=getConnection();
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try {
            statement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            for (int i=0;i<pram.length;i++){
                statement.setObject(i+1,pram[i]);
            }
            statement.executeUpdate();
            //获取主键
            resultSet = statement.getGeneratedKeys();
            if (resultSet.next()){
                int generatedKeys = resultSet.getInt(1);
                return generatedKeys;
            }
        } catch (SQLException throwables) {
            System.out.println("DBUtil:commInsert错误!");
            throwables.printStackTrace();
        }
        finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return -1;
    }

    /**
     * 插入、修改、删除
     * @param sql  sql命令
     * @param pram 参数
     */
    public static void commUpdate(String sql,Object... pram){
        Connection connection=getConnection();
        PreparedStatement statement=null;
        try {
            statement = connection.prepareStatement(sql);
            for (int i=0;i<pram.length;i++){
                statement.setObject(i+1,pram[i]);
            }
            statement.executeUpdate();
        } catch (SQLException throwables) {
            System.out.println("DBUtil:commUpdate错误!");
        }
        finally {
            DBUtil.close(connection,statement,null);
        }
    }

    /**
     * 查询
     * @param c 实体类的字节码文件对象
     * @param sql   SQL指令
     * @param param SQL参数
     * @param <T>  实体类的类型
     * @return  实体类对象的集合
     */
    public static<T> ArrayList<T> commonQuery(Class<T> c, String sql, Object... param){

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        //设置集合容器装入数据库数据
        ArrayList<T> list = new ArrayList<>();
        try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            //设置SQL参数
            for (int i = 0; i < param.length; i++) {
                statement.setObject(i+1,param[i]);
            }
            //发送SQL指令
            resultSet = statement.executeQuery();

            //获取到表单对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取字段名的个数
            int columnCount = metaData.getColumnCount();

            //遍历数据
            while(resultSet.next()){
                //利用反射创建对象
                T t = c.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    //获取字段名
                    String columnName = metaData.getColumnName(i);
                    //获取该字段名对应的数据
                    Object obj = resultSet.getObject(columnName);
                    try {
                        //返回一个属性
                        Field field = getField(c,columnName);
                        //设置属性值
                        setField(t,field,obj);

                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                }
                //装入集合
                list.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return list;
    }
    
    private static<T> Field getField(Class<T> c,String columnName) throws NoSuchFieldException {
        Field field = null;
        try {
            field = c.getDeclaredField(columnName);
            return field;
        } catch (NoSuchFieldException e) {
            //子类未找到的情况,在父类中寻找
            for (Class<? super T > superclass = c.getSuperclass();superclass!=null;superclass = superclass.getSuperclass()){
                try {
                    field = superclass.getDeclaredField(columnName);
                    return field;
                } catch (NoSuchFieldException noSuchFieldException) {
                    //父类中也未找到,在映射文件中寻找
                    String fieldName = vo.getProperty(columnName);
                    try {
                        //递归调用getField方法。
                        field = getField(c,fieldName);
                        return field;
                    } catch (NoSuchFieldException suchFieldException) {
                    }
                }
            }
        }
        throw new NoSuchFieldException();
    }
    private static<T> void setField(T t,Field field,Object obj){
        field.setAccessible(true);
        try {
            field.set(t,obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

1.9 连接池

连接池的用法与线程池相似,通过提前创建好Connection连接对象,在需要时直接拿出使用,使用完之后由连接池进行回收。

​ 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高了数据库操作的性能。

1.9.1 模拟连接池

根据以上的概念,我们能够很清晰的感受到连接池的运作过程,得出一个自己编写的模拟连接池。

线程池类

import com.dream.test03.DBUtil;

import java.sql.Connection;
import java.util.LinkedList;

//模拟线程池
public class FastConnectionPool {

    //存储连接资源(Connection)
    static LinkedList<Connection> pool = new LinkedList<>();

    //初始化数据连接资源(存储10个)
    static {
        for (int i=0;i<10;i++){
            Connection connection = DBUtil.getConnection();//此处还是使用工具类获取
            pool.addLast(connection);
        }
    }

    //提供资源
    public static Connection getConnection(){
        if (!pool.isEmpty()){
            //LinkedList的栈模式输出。
            return pool.getFirst();
        }else{
            return null;
        }
    }
    
    //回收资源
    public static void release(Connection connection){
        pool.addLast(connection);
    }
}

测试方法:

/**
 * 使用连接池的Connection连接数据库
 */
Connection connection = FastConnectionPool.getConnection();
String sql = "SELECT * FROM student WHERE age <?;";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,30);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){
    int id = resultSet.getInt("id");
    String s_name = resultSet.getString("s_name");
    int age = resultSet.getInt("age");
    System.out.println(id+"--"+s_name+"--"+age);
}
//关闭资源
statement.close();
resultSet.close();
//回收connection
FastConnectionPool.release(connection);
/*
1--小华--28
2--小川--26
6--小黄--10
*/

注:在使用模拟连接池中的connection结束后,不能直接close掉,需要重新回收到连接池中,但我们仍然不能避免使用连接池的人直接将connection.close(),这样连接池中的连接对象会越来越少,甚至还另外需要重新创建,且这已经脱离了原本使用连接池的初心。

1.9.2 连接池规范

DataSource :连接池规范

为了避免模拟连接池出现的上述问题,我们重新定义MyConnection 连接类,实现Connection接口,重写close方法,将close方法重写为回收连接对象到连接池。

注:在实现Connection接口时,有大量的方法需要我们重写,但我们主要需要重写close方法,让connection对象回收到连接池中,其他的方法可直接调用原来Connection的方法,也需要自己手动改写(这里只重写了经常用到的一些方法。)

除close方法外,重写时只需要使用connection调用对应方法即可。(因为这些方法我们不需要改变)

package com.dream.test07;

import java.sql.*;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class MyConnection implements Connection {
    private LinkedList<Connection> list;
    private Connection connection;

    public MyConnection(LinkedList<Connection> list, Connection connection) {
        this.list = list;
        this.connection = connection;
    }

    @Override
    public Statement createStatement() throws SQLException {
        return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return connection.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return connection.nativeSQL(sql);
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(autoCommit);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return connection.getAutoCommit();
    }

    @Override
    public void commit() throws SQLException {
        connection.commit();
    }

    @Override
    public void rollback() throws SQLException {
        connection.rollback();
    }

    @Override
    public void close() throws SQLException {
        //回收connection到连接池中
        list.addLast(this);
    }

    @Override
    public boolean isClosed() throws SQLException {
        return connection.isClosed();
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return connection.getMetaData();
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        connection.setReadOnly(readOnly);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return connection.isReadOnly();
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        connection.setCatalog(catalog);
    }

    @Override
    public String getCatalog() throws SQLException {
        return connection.getCatalog();
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        connection.setTransactionIsolation(level);
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return connection.getTransactionIsolation();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return null;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return null;
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

    }

    @Override
    public void setHoldability(int holdability) throws SQLException {

    }

    @Override
    public int getHoldability() throws SQLException {
        return 0;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return null;
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return null;
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {

    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {

    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return null;
    }

    @Override
    public Clob createClob() throws SQLException {
        return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
        return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
        return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return null;
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return false;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {

    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {

    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return null;
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return null;
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return null;
    }

    @Override
    public void setSchema(String schema) throws SQLException {

    }

    @Override
    public String getSchema() throws SQLException {
        return null;
    }

    @Override
    public void abort(Executor executor) throws SQLException {

    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {

    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return 0;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}

连接池类:

注:在MyFastConnectionPool中主要重写getConnection()方法,从连接池中取出connection对象。

package com.dream.test07;

import com.dream.test03.DBUtil;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

public class MyFastConnectionPool implements DataSource {
    private static LinkedList<Connection> list;

    static {
        list = new LinkedList<>();
        MyConnection myConnection = null;
        for (int i = 0; i <  20; i++) {
            myConnection = new MyConnection(list, DBUtil.getConnection());
            list.add(myConnection);
        }
    }


    @Override
    public Connection getConnection() throws SQLException {
        return list.removeFirst();
    }

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

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

测试方法:

/**
 * 使用连接池的Connection连接数据库
 */
MyFastConnectionPool pool = new MyFastConnectionPool();
Connection connection = pool.getConnection();
String sql = "SELECT * FROM student WHERE age <?;";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,30);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){
    int id = resultSet.getInt("id");
    String s_name = resultSet.getString("s_name");
    int age = resultSet.getInt("age");
    System.out.println(id+"--"+s_name+"--"+age);
}
//关闭资源
statement.close();
resultSet.close();
//回收connection
connection.close();

注:connection为父类引用,存放的是接口MyConnection的对象,再调用close方法时,会默认执行MyConnection的close方法,将connection回收到连接池中。

1.9.3 开源数据库连接池

通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:

· DBCP 数据库连接池

· C3P0 数据库连接池

· Druid(德鲁伊) 数据库连接池

在使用了数据库连接池之后,在项目的实际开发中就不需要编写连接数据库的代码了,直接从数据源获得数据库的连接

其中最常用的为德鲁伊连接池(其他连接池有隐藏的兼容性问题)

1.9.3.1 Druid连接池

Druid 是目前比较流行的高性能的,它有如下几个特点:

一. 亚秒级查询

druid提供了快速的聚合能力以及亚秒级的查询能力,多租户的设计,是面向用户分析应用的理想方式。

二.实时数据注入

druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性

三.可扩展的PB级存储

druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性

四.多环境部署

druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等

五.丰富的社区

druid拥有丰富的社区,供大家学习

1.9.3.2 DButil+druid

package com.dream.util;

import com.alibaba.druid.pool.DruidDataSource;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.Properties;

public class DBUtil {

    private static ThreadLocal<Connection> local=new ThreadLocal<>();
    private static DruidDataSource dataSource;
    private static Properties vo;

    static {
        //加载Sql配置文件
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
        } catch (IOException e) {
            System.out.println("DBUtil:配置文件读取错误!");
        }
        String driverName = properties.getProperty("driverName");
        String url = properties.getProperty("url");
        String userName = properties.getProperty("userName");
        String passWord = properties.getProperty("passWord");

        //创建德鲁伊数据库连接池
        dataSource = new DruidDataSource();

        //设置德鲁伊数据库连接池的参数
        dataSource.setDriverClassName(driverName);//加载驱动
        dataSource.setUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(passWord);
        dataSource.setMaxActive(1000);//最大连接数

        //加载数据库映射文件
        vo = new Properties();
        try {
            vo.load(DBUtil.class.getClassLoader().getResourceAsStream("VOConfig.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //获取连接对象Connection
    public static Connection getConnection(){
        if (local.get()==null){
            Connection connection = null;
            try {
                connection = dataSource.getConnection();
            } catch (SQLException e) {
                System.out.println("DBUtil:Connection连接错误!");
            }
            local.set(connection);
        }
        return local.get();
    }

    //开启事务
    public static void startTransaction(){
        try {
            getConnection().setAutoCommit(false);
        } catch (SQLException throwables) {
            System.out.println("DBUtil:事务开启错误!");
        }
    }
    //提交事物
    public static void commit() {
        Connection connection=local.get();
        if (connection!=null){
            try {
                connection.commit();
                connection.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:事务提交错误!");
            }
            local.set(null);
        }
    }
    //回滚事务
    public static void rollback() {
        Connection connection=local.get();
        if (connection!=null){
            try {
                connection.rollback();
                connection.close();
                System.out.println("DBUtil:发生事务回滚!");
            } catch (SQLException throwables) {
                System.out.println("DBUtil:回滚事务错误!");
            }
            local.set(null);
        }
    }
    //关闭资源
    public static void close(Connection connection,Statement statement,ResultSet resultSet) {
        if (resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:resultSet关闭错误!");
            }
        }
        if (statement!=null){
            try {
                statement.close();
            } catch (SQLException throwables) {
                System.out.println("DBUtil:statement关闭错误!");
            }
        }
        if (connection!=null){
            try {
                if (connection.getAutoCommit()){
                    try {
                        connection.close();
                        local.set(null);
                    } catch (SQLException throwables) {
                        System.out.println("DBUtil:connection关闭错误!");
                    }
                }
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    //主键回填
    /**
     * 插入并返回主键
     * @param sql   SQL命令
     * @param pram  参数
     * @return 主键
     */
    public static int commInsert(String sql,Object... pram){
        Connection connection=getConnection();
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        try {
            statement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            for (int i=0;i<pram.length;i++){
                statement.setObject(i+1,pram[i]);
            }
            statement.executeUpdate();
            //获取主键
            resultSet = statement.getGeneratedKeys();
            if (resultSet.next()){
                int generatedKeys = resultSet.getInt(1);
                return generatedKeys;
            }
        } catch (SQLException throwables) {
            System.out.println("DBUtil:commInsert错误!");
            throwables.printStackTrace();
        }
        finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return -1;
    }

    /**
     * 插入、修改、删除
     * @param sql  sql命令
     * @param pram 参数
     */
    public static void commUpdate(String sql,Object... pram) throws SQLException {
        Connection connection=getConnection();
        PreparedStatement statement=null;

        try {
            statement = connection.prepareStatement(sql);
            for (int i=0;i<pram.length;i++){
                statement.setObject(i+1,pram[i]);
            }
            statement.executeUpdate();
        }
        finally {
            DBUtil_1.close(connection,statement,null);
        }
    }

    /**
     * 查询
     * @param c 实体类的字节码文件对象
     * @param sql   SQL指令
     * @param param SQL参数
     * @param <T>  实体类的类型
     * @return  实体类对象的集合
     */
    public static<T> ArrayList<T> commonQuery(Class<T> c, String sql, Object... param) {

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        //设置集合容器装入数据库数据
        ArrayList<T> list = new ArrayList<>();
        try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            //设置SQL参数
            for (int i = 0; i < param.length; i++) {
                statement.setObject(i+1,param[i]);
            }
            //发送SQL指令
            resultSet = statement.executeQuery();

            //获取到表单对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取字段名的个数
            int columnCount = metaData.getColumnCount();

            //遍历数据
            while(resultSet.next()){
                //利用反射创建对象
                T t = c.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    //获取字段名
                    String columnName = metaData.getColumnName(i);
                    //获取该字段名对应的数据
                    Object obj = resultSet.getObject(columnName);
                    try {
                        //返回一个属性
                        Field field = getField(c,columnName);
                        //设置属性值
                        setField(t,field,obj);

                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                }
                //装入集合
                list.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return list;
    }
    private static<T> Field getField(Class<T> c,String columnName) throws NoSuchFieldException {
        Field field = null;
        try {
            field = c.getDeclaredField(columnName);
            return field;
        } catch (NoSuchFieldException e) {
            //子类未找到的情况,在父类中寻找
            for (Class<? super T > superclass = c.getSuperclass();superclass!=null;superclass = superclass.getSuperclass()){
                try {
                    field = superclass.getDeclaredField(columnName);
                    return field;
                } catch (NoSuchFieldException noSuchFieldException) {
                    String fieldName = vo.getProperty(columnName);
                    try {
                        field = getField(c,fieldName);
                        return field;
                    } catch (NoSuchFieldException suchFieldException) {
                    }
                }
            }
        }
        throw new NoSuchFieldException();
    }
    private static<T> void setField(T t,Field field,Object obj){
        field.setAccessible(true);
        try {
            field.set(t,obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

测试方法:

 /**
  * 使用德鲁伊数据库连接池
  */
public static void main(String[] args) {
    String sql = "select * from student";
    ArrayList<Student> students = DBUtil.commonQuery(Student.class, sql);
    for (Student stu: students ) {
        System.out.println(stu);
    }
}

1.9.3.3 拓展:使用注解获取到实体类中的属性在数据库表中的信息

详细说明:见java入门基础十五 注解和反射

重新修改DBUtil中的查询方法:

public static<T> ArrayList<T> commonQuery(Class<T> c, String sql, Object... param){

    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    //设置集合容器装入数据库数据
    ArrayList<T> list = new ArrayList<>();
    try {
        connection = getConnection();
        statement = connection.prepareStatement(sql);
        //设置SQL参数
        for (int i = 0; i < param.length; i++) {
            statement.setObject(i+1,param[i]);
        }
        //发送SQL指令
        resultSet = statement.executeQuery();

        //获取到表单对象
        ResultSetMetaData metaData = resultSet.getMetaData();
        //获取字段名的个数
        int columnCount = metaData.getColumnCount();

        //遍历数据
        while(resultSet.next()){
            //利用反射创建对象
            T t = c.newInstance();
            //先寻找子类中的所有属性
            Field[] fields = c.getDeclaredFields();
            for (Field field:
                 fields) {
                field.setAccessible(true);
                field.set(t,resultSet.getObject(field.getAnnotation(FieldInfo.class).name()));
            }
            //再寻找父类
            for (Class<? super T > superclass = c.getSuperclass();superclass!=null;superclass = superclass.getSuperclass()){
                Field[] declaredFields = superclass.getDeclaredFields();
                for (Field sf :
                     declaredFields) {
                    sf.setAccessible(true);
                    sf.set(t,resultSet.getObject(sf.getAnnotation(FieldInfo.class).name()));
                }

            }
            //装入集合
            list.add(t);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
   	finally {
        DBUtil_1.close(connection,statement,resultSet);
    }
    return list;
}

修改后直接获取到实体类属性上的注解,来代替映射文件,也不在需要getFIeld方法、setField方法和加载映射文件。

上一篇:JDBC工具类


下一篇:JQuery里ajax的表单传值serialize()用法