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("登录成功!");
}elseSystem.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 transactionConnection.rollback(); // 事务回滚 rollbackConnection.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();}}//获取连接对象Connectionpublic 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;}@Overridepublic Statement createStatement() throws SQLException {return connection.createStatement();}@Overridepublic PreparedStatement prepareStatement(String sql) throws SQLException {return connection.prepareStatement(sql);}@Overridepublic CallableStatement prepareCall(String sql) throws SQLException {return connection.prepareCall(sql);}@Overridepublic String nativeSQL(String sql) throws SQLException {return connection.nativeSQL(sql);}@Overridepublic void setAutoCommit(boolean autoCommit) throws SQLException {connection.setAutoCommit(autoCommit);}@Overridepublic boolean getAutoCommit() throws SQLException {return connection.getAutoCommit();}@Overridepublic void commit() throws SQLException {connection.commit();}@Overridepublic void rollback() throws SQLException {connection.rollback();}@Overridepublic void close() throws SQLException {//回收connection到连接池中list.addLast(this);}@Overridepublic boolean isClosed() throws SQLException {return connection.isClosed();}@Overridepublic DatabaseMetaData getMetaData() throws SQLException {return connection.getMetaData();}@Overridepublic void setReadOnly(boolean readOnly) throws SQLException {connection.setReadOnly(readOnly);}@Overridepublic boolean isReadOnly() throws SQLException {return connection.isReadOnly();}@Overridepublic void setCatalog(String catalog) throws SQLException {connection.setCatalog(catalog);}@Overridepublic String getCatalog() throws SQLException {return connection.getCatalog();}@Overridepublic void setTransactionIsolation(int level) throws SQLException {connection.setTransactionIsolation(level);}@Overridepublic int getTransactionIsolation() throws SQLException {return connection.getTransactionIsolation();}@Overridepublic SQLWarning getWarnings() throws SQLException {return null;}@Overridepublic void clearWarnings() throws SQLException {}@Overridepublic Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {return null;}@Overridepublic PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {return null;}@Overridepublic CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {return null;}@Overridepublic Map<String, Class<?>> getTypeMap() throws SQLException {return null;}@Overridepublic void setTypeMap(Map<String, Class<?>> map) throws SQLException {}@Overridepublic void setHoldability(int holdability) throws SQLException {}@Overridepublic int getHoldability() throws SQLException {return 0;}@Overridepublic Savepoint setSavepoint() throws SQLException {return null;}@Overridepublic Savepoint setSavepoint(String name) throws SQLException {return null;}@Overridepublic void rollback(Savepoint savepoint) throws SQLException {}@Overridepublic void releaseSavepoint(Savepoint savepoint) throws SQLException {}@Overridepublic Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {return null;}@Overridepublic PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {return null;}@Overridepublic CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {return null;}@Overridepublic PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {return null;}@Overridepublic PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {return null;}@Overridepublic PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {return null;}@Overridepublic Clob createClob() throws SQLException {return null;}@Overridepublic Blob createBlob() throws SQLException {return null;}@Overridepublic NClob createNClob() throws SQLException {return null;}@Overridepublic SQLXML createSQLXML() throws SQLException {return null;}@Overridepublic boolean isValid(int timeout) throws SQLException {return false;}@Overridepublic void setClientInfo(String name, String value) throws SQLClientInfoException {}@Overridepublic void setClientInfo(Properties properties) throws SQLClientInfoException {}@Overridepublic String getClientInfo(String name) throws SQLException {return null;}@Overridepublic Properties getClientInfo() throws SQLException {return null;}@Overridepublic Array createArrayOf(String typeName, Object[] elements) throws SQLException {return null;}@Overridepublic Struct createStruct(String typeName, Object[] attributes) throws SQLException {return null;}@Overridepublic void setSchema(String schema) throws SQLException {}@Overridepublic String getSchema() throws SQLException {return null;}@Overridepublic void abort(Executor executor) throws SQLException {}@Overridepublic void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {}@Overridepublic int getNetworkTimeout() throws SQLException {return 0;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic 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);}}@Overridepublic Connection getConnection() throws SQLException {return list.removeFirst();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic 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();}}//获取连接对象Connectionpublic 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方法和加载映射文件。

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

  1. Mybatis零基础教程,Java访问数据库核心操作,详解Spring-boot整合Mybatis持久层!

    1.前言 持久层是JavaEE中访问数据库的核心操作,Mybatis是一款优秀的持久层框架,诞生于2010年,2013年迁移至Github.它支持定制化 SQL.存储过程以及高级映射.MyBatis ...

  2. 一文快速回顾 Java 操作数据库的方式-JDBC

    前言 数据库的重要性不言而喻,不管是什么系统,什么应用软件,也不管它们是 Windows 上的应用程序,还是 Web 应用程序,存储(持久化)和查询(检索)数据都是核心的功能. 大家学习数据库时,比如 ...

  3. 理解Java操作数据库原理

    2019独角兽企业重金招聘Python工程师标准>>> 参考:https://blog.csdn.net/xiaozhegaa/article/details/70208646 上面 ...

  4. Java操作数据库方式(六)DataSource详解

    ##概述 在java世界里操作数据库有很多方式,在众多方式中除了JDBC外都有DataSource对象. DataSource可以看作数据源,它封装了数据库参数,连接数据库,程序中操作DataSour ...

  5. Java操作数据库方式五MyBatis使用入门

    ##概述 ##MyBatis是什么 MyBatis是一个持久层框架,作用是在java项目中操作数据库. ##MyBatis介绍 MyBatis 本是apache的一个开源项目iBatis, 2010年 ...

  6. Java操作数据库方式二DBCP使用详解

    ##概述 DBCP的全称是:DataBase connection pool,翻译是:数据库连接池. 在Java操作数据库方式一JDBC使用详解中说到直接使用JDBC非常消耗资源.为了避免频繁关闭链接 ...

  7. Java 操作数据库插入失败原因

    Java 操作数据库插入失败原因 IDEA的BUG ,我找了好久才找到 看下面一句话,写的很完美 ,没有错误. 可为啥不能执行....... 然而在箭头所指的地方加两个空格后就可以执行了. 无语了,碰 ...

  8. 数据库中clob类型对应java,java 操作数据库clob类型大字段

    java 操作数据库clob类型大字段 java 操作数据库clob类型大字段,处理工具类如下: package org.shefron.utils; import java.io.StringRea ...

  9. JDBC编程(Java操作数据库 MySQL)

    数据库提供了各种语言版本的API(一组函数/一组类),调用这些API就能操作数据库 而在Java中,又将各种数据库的api再次抽象封装成一套统一的API--JDBC 在中央仓库 Maven Repos ...

最新文章

  1. 差异表达基因火山图(ggplot函数)
  2. Thinkphp5创建控制器
  3. 传感器是大数据的重要来源
  4. MySQL面试三连杀:如何实现可重复读、又为什么会出现幻读、是否解决了幻读问题?...
  5. 【软件构造】第三章第三节 抽象数据型(ADT)
  6. Windows下怎样使用bat设置Redis和Nginx开机自启动
  7. DVWA系列之24 high级别上传漏洞
  8. python统计文件大小_python工具--01.统计当前目录下的文件的大小
  9. 服务器被攻击怎么修改,服务器一直被攻击怎么办?
  10. github 学习使用
  11. 信息学奥赛C++语言:细胞分裂
  12. 深度学习之人脸检测实践-现有框架实现 opencv Face++
  13. python怎么引入thrift文件_python使用thrift教程的方法示例
  14. 太厉害了!推荐几款 Redis 可视化工具
  15. 混合动力系统的整车经济性开发与能量管理策略高级技术
  16. “均线金叉和均线死叉”的经典战法
  17. [ECCV2020]NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis
  18. cortex系列处理器排行_arm处理器排行_ARM Cortex A系列处理器性能分类比较ARM处理器排名 ZNDS资讯...
  19. 【Verilog】马里奥小游戏的FPGA实现
  20. linux卡住重启_linux df -h 命令卡住 解决方法

热门文章

  1. 手把手教你做第一个RPA机器人流程
  2. unity3d消除你不想要的黄色警告
  3. 文件查找(c语言 findfirst函数,findnext函数)
  4. PostgreSQL数据库连接
  5. 数据库date日期转String类型
  6. Swift实现洗牌动画效果
  7. springboot+社区快递代取服务系统毕业设计-附源码051434
  8. kali pinyin拼音输入法
  9. RPA中国流程自动化产业峰会火热报名中...
  10. 第1篇:Python 环境搭建