JDBC
JDBC优化
优化方法
-
抽象方法
代码中具有相同的代码,而且代码描述一个独立的业务。
新建方法,方法中描述相同代码,在需要的地方调用。 -
抽象带参数的方法
代码中具有大量相同的代码。(不同代码可以用变量表示)
新建带参数的方法,使用参数控制变化的内容。 -
抽象父类
当多个类中具有相同的属性和方法,切满足is-a关系。
新建父类,将相同的属性和方法拷贝到父类中,子类extends父类。
1. 变量优化 → 局部变量变为全局变量
2. 重复的方法,独立业务:
优化:
方法重载。在增删改时关两个,而查询时关三个,为了方便调用进行方法重载。(参数不同、方法名相同)
代码:
package dao;
import entity.Student;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class StudentDao {
String driver ="com.mysql.jdbc.Driver";
//不同数据库、相同数据库不同版本的driver不同
String url="jdbc:mysql://localhost:3306/scott?useSSL=false";//连接what数据库
String name="root";//登录用户,一般是hoot
String pass="123456";//自己数据库的密码
public Connection getConnection(){
Connection connection = null;
try {
//注册驱动 (仅仅做一次)
Class.forName(driver);//反射
//建立连接(Connection)
connection= DriverManager.getConnection (url,name,pass);//建立连接需要四个参数
} catch (Exception e) {//直接父类
e.printStackTrace();
}
return connection;//注意返回
}
//方法重载
//DQL
public void closeAll(ResultSet resultSet,Statement statement,Connection connection){//正序开、反序关
//释放资源 (connection、statement、resultset)
try {//对象不存在关不上,需要加try/catch
//判断,若resultSet = null 控制真异常
if (resultSet != null){
resultSet.close();
}
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
}catch (SQLException throwables){
throwables.printStackTrace();
}
}
//DML
public void closeAll(Statement statement,Connection connection){//正序开、反序关
//释放资源 (connection、statement、resultset)
try {//对象不存在关不上,需要加try/catch
//判断,若*** = null 控制真异常
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
}catch (SQLException throwables){
throwables.printStackTrace();
}
}
public int insert(){//插入方法
Connection connection = null;
PreparedStatement statement = null;
int ret =-1;
try {
connection = getConnection();//接下来需要用到,故必须接收返回连接
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement("insert into student (student_name,student_no,sex) values(?,?,?)");
statement.setObject(1 ,"lily");
statement.setObject(2 ,"202205");
statement.setObject(3 ,"女");
//运行语句
ret = statement.executeUpdate();
System.out.println(ret);
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(statement,connection);
}
return ret;
}
public List<Student> queryAllStudent(){//查询方法
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs =null;
List<Student> list = new ArrayList<>();//list加泛型
try {
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement("select * from student");
//运行语句
rs = statement.executeQuery();
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
while (rs.next()){//不知道记录条数,不知循环次数——while。结果集的指针可以向下移动一步时
Student student = new Student();//实例化对象
//把rs中的每一个值都放入Student中
student.setId(rs.getInt(1));
student.setStudent_name(rs.getString("student_name"));
list.add(student);
}
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(rs,statement,connection);
}
return list;
}
}
3. 抽象带参方法(DML)优化:
增删改除了创建SQL语句以外,其余都一致 。sql(参数)以及传值(数组)。数组内 存的数据类型不同,int、varchar、data……,故数组是object类型
public int excuteinsert(String sql,Object[] objects){//插入方法
Connection connection = null;
PreparedStatement statement = null;
int ret =-1;
try {
connection = getConnection();//接下来需要用到,故必须接收返回连接
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement(sql);
for (int i=0;i< objects.length;i++){
statement.setObject(i+1,objects[i]);
}
//运行语句
ret = statement.executeUpdate();
System.out.println(ret);
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(statement,connection);
}
return ret;
}
Text
import dao.StudentDao;
public class Text {
public static void main(String[] args) {
StudentDao studentDao = new StudentDao();
studentDao.queryAllStudent().forEach(System.out::println);
studentDao.excuteinsert("insert into student (student_name,student_no,sex) values(?,?,?)",new Object[]{"baby",202207,"女"});
studentDao.excuteUpdate("update student set address = ? where id = ?",new Object[]{"北京市",4});
}
}
show
4.可变长度参数——本质:数组
Text
studentDao.excuteinsert("insert into student (student_name,student_no,sex) values(?,?,?)", "bob",202208,"男");
5.(DQL)封装优化
反射、泛型类
public List<T> executeQuery(String sql, Object[] param) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
List<T> list = new ArrayList<T>();
try {
conn = createConnction();
stat = conn.prepareStatement(sql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
stat.setObject(i + 1, param[i]);
}
}
rs = stat.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columuCount = rsmd.getColumnCount();
while (rs.next()) {
T t = (T) clazz.newInstance();
for (int i = 0; i < columuCount; i++) {
Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));
f.setAccessible(true);
f.set(t, rs.getObject(i + 1));
}
list.add(t);
}
printSql(sql, param);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn, stat, null);
}
return list;
}
5. 抽象父类
StudentDao对student表进行操作 / 此时对class表进行操作,entity中新建Clazz.java,Dao中新建clazzDao。
StudentDao与clazzDao……都存在相同的部分——抽象父类——多个类具有相同属性和方法、满足is-a关系
继承
📕 BaseDao优化JDBC 📕 👴
1>父类是抽象父类不可以实例化——abstract
1>BaseDao放入重复部分(全局变量+方法增删改),没有查询部分代码
package dao;
import java.sql.*;
public abstract class BaseDao {
String driver ="com.mysql.jdbc.Driver";
//不同数据库、相同数据库不同版本的driver不同
String url="jdbc:mysql://localhost:3306/scott?useSSL=false";//连接what数据库
String name="root";//登录用户,一般是hoot
String pass="123456";//自己数据库的密码
public Connection getConnection(){
Connection connection = null;
try {
//注册驱动 (仅仅做一次)
Class.forName(driver);//反射
//建立连接(Connection)
connection= DriverManager.getConnection (url,name,pass);//建立连接需要四个参数
} catch (Exception e) {//直接父类
e.printStackTrace();
}
return connection;//注意返回
}
//方法重载
//DQL
public void closeAll(ResultSet resultSet, Statement statement, Connection connection){//正序开、反序关
//释放资源 (connection、statement、resultset)
try {//对象不存在关不上,需要加try/catch
//判断,若resultSet = null 控制真异常
if (resultSet != null){
resultSet.close();
}
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
}catch (SQLException throwables){
throwables.printStackTrace();
}
}
//DML
public void closeAll(Statement statement,Connection connection){//正序开、反序关
//释放资源 (connection、statement、resultset)
try {//对象不存在关不上,需要加try/catch
//判断,若*** = null 控制真异常
if (statement != null){
statement.close();
}
if (connection != null){
connection.close();
}
}catch (SQLException throwables){
throwables.printStackTrace();
}
}
public int excuteinsert(String sql,Object... objects){//插入方法
Connection connection = null;
PreparedStatement statement = null;
int ret =-1;
try {
connection = getConnection();//接下来需要用到,故必须接收返回连接
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement(sql);
for (int i=0;i< objects.length;i++){
statement.setObject(i+1,objects[i]);
}
//运行语句
ret = statement.executeUpdate();
System.out.println(ret);
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(statement,connection);
}
return ret;
}
public int excuteUpdate(String sql,Object[] objects){ //修改方法
Connection connection = null;
PreparedStatement statement = null;
int ret =-1;
try {
connection = getConnection();//接下来需要用到,故必须接收返回连接
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement(sql);
for (int i=0;i<objects.length;i++){
statement.setObject(i+1,objects[i]);
}
//运行语句
ret = statement.executeUpdate();
System.out.println(ret);
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(statement,connection);
}
return ret;
}
}
package dao;
import entity.Student;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class StudentDao extends BaseDao{
public List<Student> queryAllStudent(){//查询方法
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs =null;
List<Student> list = new ArrayList<>();//list加泛型
try {
connection = getConnection();//接下来需要用到,故必须接收返回连接
//创建运行SQL的语句(Statement)
statement=connection.prepareStatement("select * from student");
//运行语句
rs = statement.executeQuery();
//处理运行结果(ResultSet)——只有查询时有,进行增删改操作时没有这一步
while (rs.next()){//不知道记录条数,不知循环次数——while。结果集的指针可以向下移动一步时
Student student = new Student();//实例化对象
//把rs中的每一个值都放入Student中
student.setId(rs.getInt(1));
student.setStudent_name(rs.getString("student_name"));
list.add(student);
}
}catch (SQLException e) {
e.printStackTrace();
}catch (Exception e) { //多重catch,最后要加一个父类
e.printStackTrace();
}finally {
closeAll(rs,statement,connection);
}
return list;
}
}
package dao;
public class ClazzDao extends BaseDao{
}
public class Text {
public static void main(String[] args) {
StudentDao studentDao = new StudentDao();
//studentDao.queryAllStudent().forEach(System.out::println);
//studentDao.excuteinsert("insert into student (student_name,student_no,sex) values(?,?,?)", "bob",202208,"男");
// studentDao.excuteUpdate("update student set address = ? where id = ?",new Object[]{"北京市",4});
ClazzDao clazzDao = new ClazzDao();
clazzDao.excuteinsert("insert into class(class_name) values (?)","班级三");
}
}
2> BaseDao 引入泛型(此时查询操作也可以写入,其余表全都继承)
数据连接池原理(线程池)
大量资源浪费在数据库连接和释放(connection和close)。使用数据连接池——实现性能优化。
常用数据连接池
dbcp:
是一个依赖Jakarta commons-pool对象池机制的数据库连接池,Tomcat的数据源使用的就是DBCP。目前 DBCP 有两个版本分别是 1.3 和 1.4。1.3 版本对应的是 JDK 1.4-1.5 和 JDBC 3,而1.4 版本对应 JDK 1.6 和 JDBC 4。因此在选择版本的时候要看看你用的是什么 JDK 版本了,功能上倒是没有什么区别。
c3p0:
是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。
Druid:多
Apache Druid 是一个分布式内存实时分析系统,用于解决如何在大规模数据集下进行快速的、交互式的查询和分析的问题。Apache Druid 由 阿里公司开发,在2019年春季被捐献给 Apache 软件基金会。
使用方法
c3p0
步骤
-
引入jar ——右键—Add as Library
-
引入mchange-commons-java…jar——右键—Add as Library
-
引入 配置文件c3p0-conflg .xml 到src根目录
修改配置
这里jdbcUrl存在问题,需要查询 -
修改代码
【上】使用static且放在getConnection()方法外,只执行一次,才能发挥出数据连接池的作用。
代码
- 在pom中引用c3p0支持
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
- 在resource中创建c3p0配置文件c3p0-config.xml
<c3p0-config>
<!-- 默认数据源 -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://host:port/db</property>
<property name="user">username</property>
<property name="password">password</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
<property name="acquireIncrement">5</property>
</default-config>
<!-- 定义带名称的数据源 -->
<named-config name="myDataSource">
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/demo</property>
<property name="user">root</property>
<property name="password">123</property>
<property name="minPoolSize">2</property>
<property name="maxPoolSize">10</property>
<property name="acquireIncrement">2</property>
</named-config>
</c3p0-config>
- 创建DBPool工具类,使用单例模式。(可以和BaseDao放在一个源文件中)
public class DBPool {
//1、声明本身类型的对象。static。
private static DBPool db = null;
private DataSource ds = new ComboPooledDataSource("myDataSource");//不带参数使用默认数据源
//2、私有化构造方法
private DBPool() {
}
//3、定义方法用于获取本身对象
public static DBPool getInstance() {
if (null == db) {
db = new DBPool();
}
return db;
}
public Connection getConn() {
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
- 修改BaseDao文件,使用连接池获得连接。
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class BaseDao<T> {
Class<T> clazz;
//反射获得clazz
public BaseDao() {
clazz = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
//关闭所有链接
public void closeAll(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//执行DML语句
public int executeUpdate(String sql, Object[] param) {
Connection conn = null;
PreparedStatement pstmt = null;
int num = 0;
try {
conn = DBPool.getInstance().getConn();//从DBPool获得连接。
pstmt = conn.prepareStatement(sql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
pstmt.setObject(i + 1, param[i]);
}
}
num = pstmt.executeUpdate();
printSql(sql, param);
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll(conn, pstmt, null);
}
return num;
}
//执行预编译DQL语句
public List<T> executeQuery(String sql, Object[] param) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
List<T> list = new ArrayList<T>();
try {
conn = DBPool.getInstance().getConn();
stat = conn.prepareStatement(sql);
if (param != null) {
for (int i = 0; i < param.length; i++) {
stat.setObject(i + 1, param[i]);
}
}
rs = stat.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columuCount = rsmd.getColumnCount();
while (rs.next()) {
T t = (T) clazz.newInstance();
for (int i = 0; i < columuCount; i++) {
Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));
f.setAccessible(true);
f.set(t, rs.getObject(i + 1));
}
list.add(t);
}
printSql(sql, param);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn, stat, null);
}
return list;
}
//执行无条件sql语句(分页使用)
public List<T> executeQuery(String sql) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
List<T> list = new ArrayList<T>();
try {
conn = DBPool.getInstance().getConn();
stat = conn.prepareStatement(sql);
rs = stat.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columuCount = rsmd.getColumnCount();
while (rs.next()) {
T t = (T) clazz.newInstance();
for (int i = 0; i < columuCount; i++) {
Field f = clazz.getDeclaredField(rsmd.getColumnName(i + 1));
f.setAccessible(true);
f.set(t, rs.getObject(i + 1));
}
list.add(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn, stat, null);
}
return list;
}
//获得所有记录条数(分页使用)
public int executeQueryCount(String sql) {
Connection conn = null;
PreparedStatement stat = null;
ResultSet rs = null;
try {
conn = DBPool.getInstance().getConn();
stat = conn.prepareStatement(sql);
rs = stat.executeQuery();
if (rs.next())
return rs.getInt(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
closeAll(conn, stat, null);
}
return 0;
}
// 输出预编译的sql语句的具体内容(便于调试)
private void printSql(String sql, Object[] params) {
StringBuffer sb = new StringBuffer(sql);
int fromIndex = 0;
if (params != null) {
for (int i = 0; i < params.length; i++) {
int index = sb.indexOf("?", fromIndex);
if (index == -1) {
sb.append(" ---> error: value too many ");
break;
}
if (params[i] instanceof String) {
sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'");
} else if (params[i] instanceof Number) {
sb.replace(index, index + 1, this.valueOf(params[i]));
} else if (params[i] instanceof Character) {
sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'");
} else if (params[i] instanceof Boolean) {
sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'");
} else if (params[i] instanceof Object[]) {
sb.replace(index, index + 1, "'" + this.valueOf(params[i]) + "'");
} else if (params[i] instanceof java.sql.Date) {
sb.replace(index, index + 1, " date '" + this.valueOf(params[i]) + "'");
} else if (params[i] instanceof java.util.Date) {
sb.replace(index, index + 1, "'java.util.Date'");
}
fromIndex = index + 1;
}
}
System.out.println(sb.toString());
}
public String valueOf(Object obj) {
return (obj == null) ? "" : obj.toString();
}
}