• Java——JDBC(Java DataBase Connectivity)数据库连接技术


    目录

    1、什么是JDBC?

    2、JDBC的本质

    3、JDBC入门案例

    3.1、前期准备

    3.2、mysql数据库准备

    3.3、测试类代码

    3.4、异常处理

    4、抽取工具类

    4.1、抽取重复代码

    4.2、抽取jdbc.properties配置文件

    5、封装实体类

    6、 解决SQL注入问题

    1、什么是JDBC?

    通过Java语言操作数据库,操作表中的数据。

    SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。

    2、JDBC的本质

    是官方(sun公司)定义的一套操作所有关系型数据库的规则(接口)。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,运行时的代码其实是驱动jar包中的实现类。

    在Java中要想访问数据库只能通过JDBC,JDBC是Java访问数据库的基础,其他数据库访问技术都是对JDBC的封装(Hibernate,MyBatis),JDBC是为了访问不同的数据库,提供了一种统一的访问方式,JDBC本身是Java连接数据库的一个标准,是进行数据库连接的抽象层。由Java编写的一组类和接口,接口的实现由各大数据库厂商来实现。

    3、JDBC入门案例

    3.1、前期准备

     为了能够测试JDBC,这里利用junit进行测试,首先创建一个JavaWeb项目,在项目src包下创建一个测试类UserTest,并导入mysql-connector的jar包,这就是JDBC的核心jar包,没有它是不能调用其中的类方法的,jar包放到WEB-INF下的lib包内。

    这里提供了mysql5版本和mysql8版本的jar包,对应的就是mysql的版本,如果是mysql5系列版本的就用5.16的jar包,如果是mysql8系列的版本就用8.0.16版本jar包。

    链接:https://pan.baidu.com/s/1loxaN41BXlfdePT_8DDBDw 
    提取码:388i

    添加到lib包下的jar包需要手动去导入一下右键jar包,点击ADD Library

    3.2、mysql数据库准备

    创建一个t_user表,自行添加数据

    1. CREATE TABLE `t_user` (
    2. `id` int(11) NOT NULL,
    3. `username` varchar(20) DEFAULT NULL,
    4. `password` varchar(20) DEFAULT NULL,
    5. `age` int(11) DEFAULT NULL,
    6. PRIMARY KEY (`id`)
    7. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    这里是我添加的数据 

    3.3、测试类代码

    前期工作准备就绪后,就开始连接数据库,编写代码了,下面就演示一下CRUD操作

    1. public class UserTest {
    2. @Test
    3. public void select_User() throws ClassNotFoundException, SQLException {
    4. //加载mysql数据库JDBC驱动
    5. Class.forName("com.mysql.jdbc.Driver");
    6. //后面test是我t_user存放的数据库名
    7. String url="jdbc:mysql://localhost:3306/test";
    8. //数据库账户
    9. String username="xxx";
    10. //数据库密码
    11. String password="xxx";
    12. //获取connection对象
    13. Connection con = DriverManager.getConnection(url, username, password);
    14. //创建statement对象
    15. Statement statement = con.createStatement();
    16. //查询语句得到一个ResultSet类型的结果集
    17. ResultSet resultSet = statement.executeQuery("select * from t_user");
    18. //遍历结果集 输出控制台 next()方法如果有结果就返回true
    19. while (resultSet.next()){
    20. int id = resultSet.getInt("id");
    21. String username1 = resultSet.getString("username");
    22. String password1 = resultSet.getString("password");
    23. int age = resultSet.getInt("age");
    24. System.out.println(id+" "+username1+" "+password1+" "+age);
    25. }
    26. //后创建的先关闭
    27. resultSet.close();
    28. statement.close();
    29. con.close();
    30. }
    31. @Test
    32. public void add_User() throws ClassNotFoundException, SQLException {
    33. Class.forName("com.mysql.jdbc.Driver");
    34. String url="jdbc:mysql://localhost:3306/test";
    35. String username="xxx";
    36. String password="xxx";
    37. //获取connection对象
    38. Connection con = DriverManager.getConnection(url, username, password);
    39. //创建statement对象
    40. Statement statement = con.createStatement();
    41. //返回int 类型 代表的是修改的条数 executeUpdate可以为insert、delete、update操作
    42. int i = statement.executeUpdate("insert into t_user value(6,'王五','wda4822',26)");
    43. System.out.println(i);
    44. //后创建的先关闭
    45. statement.close();
    46. con.close();
    47. }
    48. @Test
    49. public void delete_User() throws ClassNotFoundException, SQLException {
    50. Class.forName("com.mysql.jdbc.Driver");
    51. String url="jdbc:mysql://localhost:3306/test";
    52. String username="xxx";
    53. String password="xxx";
    54. //获取connection对象
    55. Connection con = DriverManager.getConnection(url, username, password);
    56. //创建statement对象
    57. Statement statement = con.createStatement();
    58. int i = statement.executeUpdate("delete from t_user where id=6");
    59. System.out.println(i);
    60. //后创建的先关闭
    61. statement.close();
    62. con.close();
    63. }
    64. @Test
    65. public void update_User() throws ClassNotFoundException, SQLException {
    66. Class.forName("com.mysql.jdbc.Driver");
    67. String url="jdbc:mysql://localhost:3306/test";
    68. String username="xxx";
    69. String password="xxx";
    70. //获取connection对象
    71. Connection con = DriverManager.getConnection(url, username, password);
    72. //创建statement对象
    73. Statement statement = con.createStatement();
    74. int i = statement.executeUpdate("update t_user set username='宋浩',password='555sss4',age=24 where id=5 ");
    75. System.out.println(i);
    76. //后创建的先关闭
    77. statement.close();
    78. con.close();
    79. }
    80. }

    3.4、异常处理

    在之前的代码中我们都是通过throws抛出异常,但是实际开发中需要我们去手动处理异常,下面我就以查询语句为例,演示如何处理异常

    1. public class UserTestException {
    2. @Test
    3. public void select_User() {
    4. try {
    5. Class.forName("com.mysql.jdbc.Driver");
    6. } catch (ClassNotFoundException e) {
    7. e.printStackTrace();
    8. }
    9. //因为try里面为局部变量 所以需要在外面定义初始化,为后面释放资源提供方便
    10. Connection con = null;
    11. Statement statement = null;
    12. ResultSet resultSet = null;
    13. try {
    14. String url = "jdbc:mysql://localhost:3306/test";
    15. String username = "root";
    16. String password = "xxxx";
    17. //获取connection对象
    18. con = DriverManager.getConnection(url, username, password);
    19. //创建statement对象
    20. statement = con.createStatement();
    21. //查询语句得到一个ResultSet类型的结果集
    22. resultSet = statement.executeQuery("select * from t_user");
    23. //遍历结果集 输出控制台 next()方法如果有结果就返回true
    24. while (resultSet.next()) {
    25. int id = resultSet.getInt("id");
    26. String username1 = resultSet.getString("username");
    27. String password1 = resultSet.getString("password");
    28. int age = resultSet.getInt("age");
    29. System.out.println(id + " " + username1 + " " + password1 + " " + age);
    30. }
    31. } catch (SQLException e) {
    32. e.printStackTrace();
    33. } finally {
    34. //后创建的先关闭
    35. try {
    36. if (resultSet != null) {
    37. //先判断是否为null 不为null就释放掉
    38. resultSet.close();
    39. //手动把资源赋值为null,提示gc垃圾回收机制释放资源
    40. resultSet = null;
    41. }
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. try {
    46. if (statement != null) {
    47. statement.close();
    48. statement = null;
    49. }
    50. } catch (SQLException e) {
    51. e.printStackTrace();
    52. }
    53. try {
    54. if (con != null) {
    55. con.close();
    56. con = null;
    57. }
    58. } catch (SQLException e) {
    59. e.printStackTrace();
    60. }
    61. }
    62. }
    63. }

    4、抽取工具类

    4.1、抽取重复代码

    以上代码虽然能够实现功能,但是代码的耦合度太高,比如加载驱动和定义url路径、用户、密码,以及最后的清除资源等,这都是重复工作,为了降低耦合度,我们需要把这些重复代码抽取出来到一个工具类中。

    首先创建一个utils包,里面存放的就是工具类

    1. public class JDBCUtils {
    2. //利用静态代码块去加载驱动,随着类的加载而加载且加载一次
    3. static {
    4. try {
    5. Class.forName("com.mysql.jdbc.Driver");
    6. } catch (ClassNotFoundException e) {
    7. e.printStackTrace();
    8. }
    9. }
    10. //获取连接对象
    11. public static Connection getConnection(){
    12. String url="jdbc:mysql://localhost:3306/test";
    13. String user="root";
    14. String password="xxxx";
    15. Connection con = null;
    16. try {
    17. con = DriverManager.getConnection(url, user, password);
    18. } catch (SQLException e) {
    19. e.printStackTrace();
    20. }
    21. return con;
    22. }
    23. //释放查询语句资源
    24. public static void close(ResultSet rs,Statement stmt,Connection con){
    25. try {
    26. if (rs != null) {
    27. rs.close();
    28. rs = null;
    29. }
    30. } catch (SQLException e) {
    31. e.printStackTrace();
    32. }
    33. try {
    34. if (stmt != null) {
    35. stmt.close();
    36. stmt = null;
    37. }
    38. } catch (SQLException e) {
    39. e.printStackTrace();
    40. }
    41. try {
    42. if (con != null) {
    43. con.close();
    44. con = null;
    45. }
    46. } catch (SQLException e) {
    47. e.printStackTrace();
    48. }
    49. }
    50. //释放insert、delete、update语句的资源
    51. public static void close(Statement stmt,Connection con){
    52. try {
    53. if (stmt != null) {
    54. stmt.close();
    55. stmt = null;
    56. }
    57. } catch (SQLException e) {
    58. e.printStackTrace();
    59. }
    60. try {
    61. if (con != null) {
    62. con.close();
    63. con = null;
    64. }
    65. } catch (SQLException e) {
    66. e.printStackTrace();
    67. }
    68. }
    69. }

    我们再去测试功能时,代码就会得到简化,下面就是使用工具类以后 

    1. @Test
    2. public void select_user(){
    3. Connection con = JDBCUtils.getConnection();
    4. Statement stmt = null;
    5. ResultSet rs = null;
    6. try {
    7. stmt = con.createStatement();
    8. rs = stmt.executeQuery("select * from t_user");
    9. while (rs.next()) {
    10. int id = rs.getInt("id");
    11. String username = rs.getString("username");
    12. String password = rs.getString("password");
    13. int age = rs.getInt("age");
    14. System.out.println(id + " " + username + " " + password + " " + age);
    15. }
    16. } catch (SQLException e) {
    17. e.printStackTrace();
    18. } finally {
    19. JDBCUtils.close(rs,stmt,con);
    20. }
    21. }

    4.2、抽取jdbc.properties配置文件

    目前还是有一点尚且优化的地方,就是我们填写jdbc的信息,以后肯定是会改变这些信息的,为了方便维护和查看,我们需要把这些信息抽取出来到一个配置文件进行存放,然后通过获取这个配置文件去加载内容。

    在src目录下创建一个文件名为jdbc.properties

    1. jdbc.className=com.mysql.jdbc.Driver
    2. jdbc.url=jdbc:mysql://localhost:3306/test
    3. jdbc.user=root
    4. jdbc.password=xxxx

    在工具类中进行优化

    1. public class JDBCUtils2 {
    2. public JDBCUtils2() {
    3. }
    4. private static String className;
    5. private static String url;
    6. private static String username;
    7. private static String password;
    8. private static Connection con = null;
    9. static {
    10. //初始化properties对象
    11. Properties pro = null;
    12. InputStream is=null;
    13. try {
    14. //通过反射获取配置文件io对象
    15. is = JDBCUtils2.class.getClassLoader().getResourceAsStream("jdbc.properties");
    16. pro = new Properties();
    17. //调用load方法读取jdbc.properties配置文件内容
    18. pro.load(is);
    19. } catch (IOException e) {
    20. e.printStackTrace();
    21. } finally {
    22. try {
    23. is.close();
    24. } catch (IOException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. //分别调用getProperty方法获取对应的value值
    29. className = pro.getProperty("jdbc.className");
    30. url = pro.getProperty("jdbc.url");
    31. username = pro.getProperty("jdbc.user");
    32. password = pro.getProperty("jdbc.password");
    33. //加载驱动
    34. try {
    35. Class.forName(className);
    36. } catch (ClassNotFoundException e) {
    37. e.printStackTrace();
    38. }
    39. //创建连接
    40. try {
    41. con = DriverManager.getConnection(url, username, password);
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. public static Connection getConnection() {
    47. return con;
    48. }
    49. //释放查询语句资源
    50. public static void close(ResultSet rs, Statement stmt, Connection con) {
    51. try {
    52. if (rs != null) {
    53. rs.close();
    54. rs = null;
    55. }
    56. } catch (SQLException e) {
    57. e.printStackTrace();
    58. }
    59. try {
    60. if (stmt != null) {
    61. stmt.close();
    62. stmt = null;
    63. }
    64. } catch (SQLException e) {
    65. e.printStackTrace();
    66. }
    67. try {
    68. if (con != null) {
    69. con.close();
    70. con = null;
    71. }
    72. } catch (SQLException e) {
    73. e.printStackTrace();
    74. }
    75. }
    76. //释放insert、delete、update语句的资源
    77. public static void close(Statement stmt, Connection con) {
    78. try {
    79. if (stmt != null) {
    80. stmt.close();
    81. stmt = null;
    82. }
    83. } catch (SQLException e) {
    84. e.printStackTrace();
    85. }
    86. try {
    87. if (con != null) {
    88. con.close();
    89. con = null;
    90. }
    91. } catch (SQLException e) {
    92. e.printStackTrace();
    93. }
    94. }
    95. }

    测试类代码 

    1. @Test
    2. public void delete_user(){
    3. Connection con = JDBCUtils2.getConnection();
    4. Statement stmt = null;
    5. try {
    6. stmt = con.createStatement();
    7. int i = stmt.executeUpdate("delete from t_user where id=5");
    8. System.out.println(i);
    9. } catch (SQLException e) {
    10. e.printStackTrace();
    11. } finally {
    12. JDBCUtils2.close(stmt,con);
    13. }
    14. }

    5、封装实体类

    首先创建一个bean包,用来存放实体类

    1. public class User {
    2. private String username;
    3. private String password;
    4. private int age;
    5. public User() {
    6. }
    7. public User(String username, String password, int age) {
    8. this.username = username;
    9. this.password = password;
    10. this.age = age;
    11. }
    12. public String getUsername() {
    13. return username;
    14. }
    15. public void setUsername(String username) {
    16. this.username = username;
    17. }
    18. public String getPassword() {
    19. return password;
    20. }
    21. public void setPassword(String password) {
    22. this.password = password;
    23. }
    24. public int getAge() {
    25. return age;
    26. }
    27. public void setAge(int age) {
    28. this.age = age;
    29. }
    30. @Override
    31. public String toString() {
    32. return "User{" +
    33. "username='" + username + '\'' +
    34. ", password='" + password + '\'' +
    35. ", age=" + age +
    36. '}';
    37. }
    38. }

    测试类 

    1. public class UserTest3 {
    2. @Test
    3. public void queryUser(){
    4. Connection conn = JDBCUtils2.getConnection();
    5. User user=null;
    6. Statement stmt=null;
    7. ResultSet rs=null;
    8. List list=new ArrayList<>();
    9. try {
    10. stmt = conn.createStatement();
    11. rs = stmt.executeQuery("select * from t_user");
    12. while(rs.next()){
    13. user=new User();
    14. user.setUsername(rs.getString("username"));
    15. user.setPassword(rs.getString("password"));
    16. user.setAge(rs.getInt("age"));
    17. list.add(user);
    18. }
    19. } catch (SQLException e) {
    20. e.printStackTrace();
    21. } finally {
    22. JDBCUtils2.close(rs,stmt,conn);
    23. }
    24. //遍历list集合
    25. list.forEach(System.out::println);
    26. }
    27. }

     

    6、 解决SQL注入问题

    先来看这一问题

    1. public class UserTest3 {
    2. @Test
    3. public void queryUser(){
    4. Connection conn = JDBCUtils2.getConnection();
    5. User user=null;
    6. Statement stmt=null;
    7. ResultSet rs=null;
    8. List list=new ArrayList<>();
    9. try {
    10. stmt = conn.createStatement();
    11. rs = stmt.executeQuery("select * from t_user where username=''or'1'='1'");
    12. while(rs.next()){
    13. user=new User();
    14. user.setUsername(rs.getString("username"));
    15. user.setPassword(rs.getString("password"));
    16. user.setAge(rs.getInt("age"));
    17. list.add(user);
    18. }
    19. } catch (SQLException e) {
    20. e.printStackTrace();
    21. } finally {
    22. JDBCUtils2.close(rs,stmt,conn);
    23. }
    24. //遍历list集合
    25. list.forEach(System.out::println);
    26. }
    27. }

    我在查询语句后面加了一个条件为username=''or'1'='1',我们现在运行程序

    发现所有信息被查询出来了,这是怎么一回事呢,这就是SQL注入问题,是一个严重的安全隐患,那我们就要开始解决这个BUG,这也是字符串拼接带来的坏处,为了解决这个BUG,就要想办法不用字符串拼接,下面就讲述一下preparedStatment方法,其实现是通过占位符来完成的。

    改造代码如下

    1. public class UserTest3 {
    2. @Test
    3. public void queryUser() {
    4. Connection conn = JDBCUtils2.getConnection();
    5. User user = null;
    6. // Statement stmt=null;
    7. PreparedStatement pstmt = null;
    8. ResultSet rs = null;
    9. List list = new ArrayList<>();
    10. try {
    11. // stmt = conn.createStatement();
    12. // rs = stmt.executeQuery("select * from t_user where username=''or'1'='1'");
    13. //创建pstmt对象并赋值sql语句
    14. pstmt = conn.prepareStatement("select * from t_user where username= ?");
    15. //为占位符进行赋值,左边是占位符索引从1开始,右边为字符串内容
    16. pstmt.setString(1,"''or'1'='1'");
    17. rs = pstmt.executeQuery();
    18. while (rs.next()) {
    19. user = new User();
    20. user.setUsername(rs.getString("username"));
    21. user.setPassword(rs.getString("password"));
    22. user.setAge(rs.getInt("age"));
    23. list.add(user);
    24. }
    25. } catch (SQLException e) {
    26. e.printStackTrace();
    27. } finally {
    28. JDBCUtils2.close(rs, pstmt, conn);
    29. }
    30. //遍历list集合
    31. list.forEach(System.out::println);
    32. }
    33. }

    可见,这次再输入就查询不到了。 

  • 相关阅读:
    libnice 源码分析
    jvm之类加载
    机器学习笔记 - 构建自己的视频分类模型的分步教程
    跟着团子学SAP PPM-项目组合概览-项目组合结构
    单目标应用:猎豹优化算法(The Cheetah Optimizer,CO)求解微电网优化MATLAB
    初识云计算
    HTML+CSS+JS制作一个迅雷看看电影网页设计实例 ,排版整洁,内容丰富,主题鲜明,简单的网页制作期末作业
    sync.Pool:提高Go语言程序性能的关键一步
    青龙面板之KS普通版、极速版【详细教程】
    【git操作】
  • 原文地址:https://blog.csdn.net/select_myname/article/details/126650631