当前位置: 首页 > news >正文

继MySQL之后的技术-JDBC-从浅到深-02

目录

概念

编程六部曲

SQL注入和statement

工具类的封装

JDBC事务

模糊查询

批处理

数据库连接池

Apache-DBUtils

BasicDao


概念

JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题。

Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序得到数据库系统,从而完成对数据库的各种操作。

解耦合:降低程序的耦合度,提高程序的扩展力。

编程六部曲

第一步,注册驱动

第二步,获取连接

第三步,获取数据库操作对象

第四步,执行sql语句

第五步,处理查询结果集

第六步,释放资源

public static void main(String[] args) throws Exception{//类加载Class.forName("com.mysql.jdbc.Driver");//获取连接String url = "jdbc:mysql://localhost:3306/mydata";Connection connection = DriverManager.getConnection(url, "root", "123456");//获取数据库对象Statement statement = connection.createStatement();//执行sql语句String sql = "select no,name from student";ResultSet resultSet = statement.executeQuery(sql);//处理结果集while(resultSet.next()){int no = resultSet.getInt(1);String name = resultSet.getString(2);System.out.println(name + ":\t" + no);}//关闭流resultSet.close();statement.close();connection.close();
}

SQL注入和statement

目前存在的问题:用户输入的信息中患有sql语句的关键字,并且这些关键字参与sql语句的编译过程导致sql语句的愿意被扭曲,进而达到sql注入。

如何解决sql注入问题?

1、主要用户提供的信息不参与sql语句的编译过程,问题就解决了。

2、即使用户提供的信息中含有sql语句的关键字,但是没有参与编译,不起作用。

3、想要用户信息不参与sql语句的编译,那么必须使用java.sql.PreparedStatement

4、PreparedStatement接口继承了java.sql.Statement

5、PreparedStatement是属于预编译的数据库操作对象

6、PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值

解决SQL注入的关键是什么?

用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。

 对比Statement和PreparedStatement

Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。

Statement是编译一次执行一次。

PreparedStatement是编译一次,可执行N次,PreparedStatement效率较高。

PreparedStatement使用较多,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。

工具类的封装

public class JDBCUtil{private static String url;private static String user;private static String password;private static String driver;static{try{Properties properties = new Properties();properties.load(new FileInputStream("mysql.properties"));driver = properties.getProperty("driver");url = properties.getProperty("url");user = properties.getProperty("user");password = properties.getProperty("password");//注册驱动Class.forName(driver);}catch (Exception e){throw new RuntimeException(e);}}//创建连接方法public static Connection getConnection(){try{return DriverManager.getConnection(url,user,password);} catch(SQLException throwables) {throw new RuntimeException(throwables);}}//关闭流public static void close(ResultSet rs, Statement ps,Connection c){if(rs != null){try{rs.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(ps != null){try{ps.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(c != null){try{c.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}}public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;String sql = "insert into t1 values('李四',2)";try{preparedStatement = connection.prepareStatement(sql);//执行sql语句preparedStatement.executeUpdate();} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null,preparedStatement,connection);}}}

JDBC事务

JDBC中的事务是自动提交的,什么时候自动提交?

只要执行任意一条DML预计,则自动提交一次,这是JDBC默认的事务行为。但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败。

//重点三行代码
connection.setAutoCommit(false);
connection.commit();
connection.rollback();
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try {//设置为不自动提交,即开启事务connection.setAutoCommit(false);String sql = "update t1 set no = no - 100 where name = '马云'" ;preparedStatement = connection.prepareStatement(sql);preparedStatement.executeUpdate();String sql2 = "update t1 set no = no + 100 where name = '马化腾'";PreparedStatement preparedStatement1 = connection.prepareStatement(sql2);preparedStatement1.executeUpdate();//提交事务connection.commit();} catch (SQLException throwables){try{//程序执行到此处,说明程序报错了connection.rollback();} catch (SQLException e){e.printStackTrace();}throwables.printStackTrace();} finally{JDBCUtil.close(null, preparedStatement, connection);}
}

模糊查询

public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;ResultSet resultSet = null;try{String sql = "select name fron t1 where name like ?";preparedStatement = connection.prepareStatement(sql);//模糊查询preparedStatement.setString(1,"马%");resultSet = preparedStatement.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name);}} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(resultSet, preparedStatement, connection);}
}

批处理

1、当需要成批插入或者更新记录时,可以采用java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理。

2、JDBC的批量处理语句包括以下方法:

        addBatch(); //添加需要批量处理的SQL语句或参数

        executeBatch(); //执行批量处理语句

        clearBatch(); // 清空批处理包下的语句

3、JDBC连接MySQL时,如果要使用批处理功能,请在url后添加:

        ?rewriteBatchedStatements=true

4、批处理往往和PreparedStatement一起搭配使用,减少变异次数,减少运行次数。

//以下是传统方法批量传入大量数据
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try{String sql = "insert into t1 values(?,?)";preparedStatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();//批量插入数据for(int i = 0;i < 5000;i++){prepreadStatement.setString(1,"冻梨");preparedStatement.setInt(2,i);preparedStatement.executeUpdate();}long end = System.currentTimeMillis();} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null, preparedStatement, connection);}
}
//以下是批处理方法
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try {String sql = "insert into t1 values(?,?)";preparedStatement = connection.preparedStatement(sql);long start = System.currentTimeMillis();//批量插入数据for(int i = 15000; i< 20000;i++){preparedStatement.setString(1,"冻梨");preparedStatement.setInt(2,i);//将sql语句加入批处理包内preparedStatement.addBatch();//达到一千条,在进行处理if((i + 1) % 1000 == 0){preparedStatement.executeBatch();preparedStatement.clearBatch();}}long end = System.currentTimeMillis();} catch (SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null,preparedStatement, connection);}
}

数据库连接池

1、传统的JDBC数据库连接使用DriverManager来获取,每次向数据库连接的时候都要将Connection加载到内容中,再验证IP地址,用户名和密码需要数据库连接,会占用很多系统资源。

2、每一次数据库连接,使用完后都得断开,如果程序出现异常而未能关闭,将导致数据库内存泄漏,最终将导致重启数据库。

3、传统获取连接的方式,不能控制创建的连接数量,如连接过多,也可能导致内存泄漏,MySQL崩溃。

4、解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

数据库连接池基本介绍:

        1、预先在缓冲池放入一定数据的连接,当需要建立数据库连接时,只需从缓冲池中取出一个,使用完毕之后再放回去。

        2、数据库连接池负责分配,管理和释放数据库连接,他允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

        3、当应用程序向连接池请求的连接数超过最大连接数据时,需要等待。

JDBC的数据库连接池使用javax.sql.DataSource来表示,DataSource只是一个接口。

数据库连接池种类:

        1、C3P0:速度慢,稳定

        2、DBCP:较快,不稳定

        3、Proxool:有监控连接池窗台的功能,不稳定

        4、BonCP:数据快

        5、Druid:集以上优点于一身

//传统方式
public static void main(String[] args) throws Exception{long start = System.currentTimeMillis();for(int i = 0;i < 5000;i++){Connection connection = JDBCUtil.getConnection();connection.close();}long end = System.currentTimeMillis();}
//连接池,需要加入对应的jar和配置文件
public static void main(String[] args) throws Exception{//获取数据源对象ComboPooledDataSource comboPoolDataSource = new ComboPooledDataSource();//根据配置文件获取信息Properties properties = new Properties();properties.load(new FileInputStream("src//mysql.properties"));String driver = properties.getProperty('driver');String url = properties.getProperty('url');String user = properties.getProperty('user');String password = properties.getProperty('password');//给数据源设置参数comboPooledDataSource.setDriverClass(driver);comboPooledDataSource.setJdbcUrl(url);comboPooledDataSource.setUser(user);comboPooledDataSource.setPassword(password);//初始化连接数comboPooledDataSource.setInitialPoolSize(10);//设置最大连接数comboPooledDataSource.setMaxPoolSize(50);//获取连接for(int i = 0;i < 5000;i++){Connection connection = comboPooledDataSource.getConnection();connection.close();}}
//比如50w,Druid要快
public static void main(String[] args) throws Exception{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));//创建一个Druid连接DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);for(int i = 0;i < 5000;i++){Connection connection = dataSource.getConnection();connection.close();}}
//关于Druid连接池的工具类
public class JDBCUtilsByDruid{private static DataSource ds;static {try{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));ds = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e){throw new RuntimeException(e);}}//创建连接public static Connection getConnection(){try{return ds.getConnection();} catch(SQLException throwables){throw new RuntimeException(throwables);}}//不是真正的关闭,而是放回连接池public static void close(ResultSet rs, Statement ps, Connection c){if(rs != null){try{rs.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(ps != null){try{ps.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(c!= null){try{c.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}}
}

Apache-DBUtils

//以下是土方法把resultSet集合里的数据,封装到list集合中
public class T2{private Integer no;private String name;public T2(){}public T2(Integer no, String name){this.no = no;this.name = name;}public Integer getNo(){return no;}public static void main(String[] args){Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;String sql = "select * from t2";//存储结果ArrayList<T2> list = new ArrayList<>();try{connection = JDBCUtilsByDruid.getConnection();preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();while(resultSet.next()){int no = resultSet.getInt(1);String name = resultSet.getString(2);list.add(new T2(no,name));}} catch(SQLExceotion throwables){throwables.printStackTrace();} finally{JDBCUtilByDruid.close(resultSet, preparedStatement, connection);}}
}

Apache-DBUtils:是Apache组织提供的一个开源JDBC工具类库,是对JDBC的封装,极大简化JDBC工作量。

Dbutils类:

1、QueryRunner类:该类封装了SQL的执行,是线程安全的。

2、ResultSetHandler接口:该接口用于处理java.sql.ResultSet,将数据按要求转换为另一种形式。

ArrayHandler:把结果集中的第一行数据转成对象数组

ArrayListJamdler:把结果集中的每一行数据都换成一个数组,存放到List中

BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中

BeanListHandler:将结果集中的第一行数据都封装到一个对应得到JavaBean实例中,存放到List

ColumnListHandler:将结果集中某一列的数据存放到List中

KeyedHandler:将结果集中的每行数据都封装到Map里,再把这些map再存放到一个map里,其key为指定的key

MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值

MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List

public static void main(String[] args) throws SQLException{Connection connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "select * from t2";/*第一个参数:一个连接对象第二个参数:sql语句第三个参数:new BeanListHandler<>(T2.class)在将resultSet封装成T2对象,然后加入list集合中底层使用反射机制,去获取T2类的属性,进行封装*/List<T2> list = queryRunner.query(connection, sql, new BeanListHandler<>(T2.class));for(T2 t: list){System.out.println(t);}JDBCUtilsByDruid.close(null,null,connection);
}
public static void main(String[] args) throws SQLException{Connection connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "delete from t2 where no = 3";int update = queryRunner.update(connection, sql);JDBCUtilsByDruid.close(null,null,connection);
}

BasicDao

DAO和增上改查通用方法——BasicDao

1、DAO:data access object 数据访问对象

2、这样的通用类,成为BasicDao,是专门和数据库交互的,即完成对数据表的crud操作

3、在BasicDao的基础上,实现一张表对应一个Dao,更好的完成功能

Customemer表——Customer.java——CustomerDao,java

Apache-dbutils+Druid简化了JDBC开发,但还有不足:

1、SQL语句是固定的,不能通过参数传入,通用性不好,需要进行改进,更方便执行CRUD

2、对于select操作,如果有返回值,返回类型不能固定,需要使用泛型

3、将来的表很多,业务需求复杂,不可能只靠一个java类完成

public class BasicDAO<T> {private QueryRunner qr = new QueryRunner();//dml操作语句public int update(String sql, Object... parameters){Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.update(connection, sql, parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}//根据传入的Class对象,返回对应泛型的集合public List<T> query(String ssql, Class<T> clazz, Object... parameters){Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanListHandler<T>(clazz),parameters);} catch (SQLException e){throw new RuntimeException(e);} finally{JDBCUtilsByDruid.close(null,null,connection);}}//查询单行结果public T querySingle(String sql, Class<T> clazz,Object... parameters){Connection connection = null;try{conenction = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}//查询单行单列结果public Object queryScalar(String sql, Object... parameters){Connection connection = null;try{conenction = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new ScalarHandler(), parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}}
public class T2DAO extends BasicDAO<T2>{}public class T2{private Integer no;private String name;public T2(){}public T2(Integer no, String name){this.no = no;this.name = name;}public Integer getNo(){ return no;}public void setNo(Integer no) {this.no = no;}public String getName() {return name;}public void setName(String name){this.name = name;}@Overridepublic String toString(){}
}public class TestDao{public static void main(String[] args){T2DAO t2DAO = new T2DAO();//查询多行String sql = "select * from t2";List<T2> query = t2DAO.query(sql, T2.class);for(T2 t: query){System.out.println(t);}//查询单行    String sql3 = "select * from t2 where no = 2";T2 t2 = t2DAO.querySingle(sql3, T2.class);//查询单行单列String sql4 = "select name fron t2 where no = 2";Object o = t2DAO.queryScalar(sql4);//dml操作String sql2 = "insert into t2 values(4, '擦原配')";int update = t2DAO.update(sql2);}
}
public class JDBCUtilsByDruid{private static DataSource ds;static{try{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));ds = DruidDataSourceFactory.createDataSource(Properties);} catch (Exception e){throw new RuntimeException(e);}}public static Connection getConnection(){try{return ds.getConnection();} catch (SQLException throwables){throw new RuntimeException(throwables);}}
}

http://www.xdnf.cn/news/893863.html

相关文章:

  • PS--钢笔工具的用法
  • YOLOv11 | 注意力机制篇 | 可变形大核注意力Deformable-LKA与C2PSA机制
  • Android Compose PrimaryTabRow、SecondaryTabRow (TabRow)自定义
  • PH热榜 | 2025-06-05
  • zynq远程更新程序
  • Day 40训练
  • LLaMA-Factory和python版本的兼容性问题解决
  • 【时时三省】(C语言基础)多维数组名作函数参数
  • 【快餐点餐简易软件】佳易王快餐店点餐系统软件功能及操作教程
  • 2025年可持续发展与环境工程国际会议(SDEE 2025)
  • 老旧热泵设备智能化改造:Ethernet IP转Modbus的低成本升级路径
  • 亚马逊:产品被顾客投诉二手产品的申诉模板
  • cuda数据传输
  • 五、Sqoop 增量导入:精通 Append 与 Lastmodified 模式
  • 【案例】电商系统的AI微服务架构设计
  • 第2天:认识LSTM
  • bootstrap:点击回到顶部 超简单
  • Modbus转Ethernet IP深度解析:磨粉设备效率跃升的底层技术密码
  • CppCon 2015 学习:C++ in the audio industry
  • 风云二号FY-2H:探秘第一代静轨气象卫星的旗舰风采
  • 动静态库的使用(Linux下)
  • 代码随想录 算法训练 Day23:回溯算法part02
  • 体积云完美融合GIS场景~提升视效
  • 使用 Inno 打包程序且安装 VC 运行时库
  • 人工智能100问☞第41问:什么是边缘AI?
  • RPM 数据库修复
  • 6.824 lab1
  • std::shared_ptr 与 std::unique_ptr 删除器设计差异
  • MATLAB | 绘图复刻(十九)| 轻松拿捏 Nature Communications 绘图
  • C++信号处理程序解析与改进