• JAVA12_06学习总结(JDBC,工具类优化)


    今日内容

    1. PreparedStatement

    PreparedStatement
        --预编译
        步骤
            1)注册驱动
            2)获取数据库连接对象
            3)准备sql语句
                --不需要拼接
                --需要的参数全部使用 ? 占位符
            4)通过数据库连接对象,获取预编译对象,同时将sql语句房费数据库,将参数和参数类型都存储在预编译中
                Connection中的方法 
                    PreparedStatement prepareStatement(String sql)
            5)给参数赋值
                void setXX(int Index,XX实际值)
                    index -- 代表第几个参数
                    实际值 -- 就是参数的实际值
            6)执行预编译对象 --在这里不用将sql语句给进去,因为第4步已经将语句传过去了,只需要执行即可
                int executeUpdate()
                ResultSet executeQuery()
            7)释放资源
    1. import utils.JdbcUtils;
    2. import java.sql.Connection;
    3. import java.sql.PreparedStatement;
    4. import java.sql.SQLException;
    5. //预编译
    6. public class preparedStatementTest {
    7. public static void main(String[] args) throws SQLException {
    8. //注册驱动,获取数据库连接对象
    9. Connection connection = JdbcUtils.getConnection();//调用工具类方法即可!
    10. //准备sql语句--使用?占位符,不需要拼接,大大提升安全性
    11. String sql = "insert into student values (?,?,?,?,?) ;" ;
    12. //获取执行对象--将sql语句传到数据库进行预编译--已经给数据库传过去了
    13. PreparedStatement preparedStatement = connection.prepareStatement(sql);
    14. //给参数赋值
    15. preparedStatement.setInt(1,5); //一号参数id,赋值为5
    16. preparedStatement.setString(2,"胡桃"); //二号参数name,赋值胡桃
    17. preparedStatement.setInt(3,18); //三号参数age,赋值18
    18. preparedStatement.setString(4,"女"); //四号参数gender,赋值女
    19. preparedStatement.setString(5,"往生堂"); //五号参数address,赋值往生堂
    20. //执行语句
    21. preparedStatement.executeUpdate();//这里不给sql,sql已经传过去了
    22. //释放资源
    23. JdbcUtils.close(preparedStatement,connection);
    24. }
    25. }

    1.1 面试题

    Statement和PreparedStatement的区别
        1)Statement
            --每次书写一条sql就需要通过Statment将sql语句发送给数据库
                -效率低并且数据库的压力大!
            --发送的sql语句存在字符串拼接
                -非常不安全--sql注入!--获取全部数据!
        2)PreparedStatement
            --将参数化的sql语句发送给数据库,进行预编译,以后执行语句只需要赋值即可,不需要重新传sql
                -效率高,数据库压力小
            --参数化的sql语句中,参数全部使用?占位符来代替,不存在拼接
                -安全

    1.2 SQL注入

    SQL注入安全问题
        在JDBC使用Statement获取执行对象,并将sql语句发送给数据库的过程中
            -用户利用sql拼接的漏洞,将用户名和密码全部绕过!
            举例
                select * from user where username='helloworld' and password = 'hello 'or '1'='1' ;
                    --利用or的"或"特性--1是常量,恒成立--直接绕过用户名密码--直接访问其他人全部数据!
            解决方案
                全部使用PreparedStatement来进行后续过程!

    2. JDBC方式控制事务

    JDBC方式控制事务
        JDBC中如果不主动设置,默认自动提交!
            public void setAutoCommit(boolean autoCommit)
                --手动设置是否自动提交
                    参数boolean autoCommit
                        -true,自动提交
                        -false,手动提交
            public void rollBack()
                --事务回滚,撤销之前的所有更新操作--前提是必须手动提交!
            public void commit()
                --手动提交
    JDBC自动提交可能会出现的问题
        不控制事务 
            --执行多个sql语句期间出问题了,会造成数据紊乱!
    1. import utils.JdbcUtils;
    2. import java.sql.Connection;
    3. import java.sql.PreparedStatement;
    4. import java.sql.SQLException;
    5. public class JDBCTransaction {
    6. public static void main(String[] args) throws SQLException {
    7. //利用表中数字增加减少举例
    8. //获取连接数据库
    9. Connection connection = JdbcUtils.getConnection();
    10. //准备sql语句
    11. //数字减少语句
    12. String sql1 = "update student set age = age - 100 where id = ? ; " ;
    13. //数字增加语句
    14. String sql2 = "update student set age = age + 100 where id = ? ; " ;
    15. //获取执行对象
    16. PreparedStatement ps1 = connection.prepareStatement(sql1);
    17. PreparedStatement ps2 = connection.prepareStatement(sql2);
    18. //参数赋值
    19. ps1.setInt(1,1);//给表中id为1的人减去100
    20. ps2.setInt(1,2);//给表中id为2的人加上100
    21. //执行
    22. ps1.executeUpdate();//执行减
    23. //制造错误
    24. int a = 1/0 ;//测试用错误
    25. ps2.executeUpdate();//执行加
    26. /*
    27. 执行结果报错,但是数据库内容,该减的减了,该加的没加
    28. */
    29. }
    30. }

    解决方案
        手动开启提交,利用回滚解决安全问题
    1. import utils.JdbcUtils;
    2. import java.sql.Connection;
    3. import java.sql.PreparedStatement;
    4. import java.sql.SQLException;
    5. public class JDBCTransaction {
    6. public static void main(String[] args) {
    7. //利用表中数字增加减少举例
    8. //获取连接数据库
    9. Connection connection = null ;
    10. PreparedStatement ps1 = null ;
    11. PreparedStatement ps2 = null ;
    12. try {
    13. connection = JdbcUtils.getConnection();
    14. //准备sql语句
    15. //数字减少语句
    16. String sql1 = "update student set age = age - 100 where id = ? ; " ;
    17. //数字增加语句
    18. String sql2 = "update student set age = age + 100 where id = ? ; " ;
    19. //获取执行对象
    20. ps1 = connection.prepareStatement(sql1);
    21. ps2 = connection.prepareStatement(sql2);
    22. //参数赋值
    23. ps1.setInt(1,1);//给表中id为1的人减去100
    24. ps2.setInt(1,2);//给表中id为2的人加上100
    25. //手动开启提交
    26. connection.setAutoCommit(false);//false为手动提交
    27. //执行
    28. ps1.executeUpdate();//执行减
    29. //制造错误
    30. int a = 1/0 ;//测试用错误
    31. ps2.executeUpdate();//执行加
    32. connection.commit();
    33. } catch (SQLException throwables) {
    34. try {
    35. //加入回滚,保证数据出错时一切回到未改变前!保证数据安全
    36. connection.rollback();
    37. } catch (SQLException e) {
    38. e.printStackTrace();
    39. }
    40. throwables.printStackTrace();
    41. }finally {
    42. JdbcUtils.close(ps1,connection);
    43. JdbcUtils.close(ps2,connection);
    44. }
    45. /*
    46. 执行结果报错 ,但是数据库没有任何改变,保护数据安全
    47. */
    48. }
    49. }

    3. 数据库连接池DateSource

    数据库连接池DataSource
        DataSource可以看作数据源,它封装了数据库参数,连接数据库
            -程序中操作DataSource对象即可对数据库进行增删改查操作
        DataSource连接池--类比线程池
            连接池在创建的时候会带默认参数--从配置文件中获取
                默认创建一定数量的数据库连接对象
                每当使用完毕后会回到连接池中,等到下次继续使用!
    Druid 德鲁伊!
        连接池工具--jar包
        来实现Java提供的DataSource接口
            参数
                driverClassName     创建驱动链接--com.mysql.jdbc.Driver
                url                 连接数据库链接--jdbc:mysql://localhost:3306/库名
                username            数据库用户名
                password            数据库密码
                initialSize         定义初始化连接数
                maxAction           最大连接数量
                maxWait             连接等待时间(毫秒值)--超过等待时间直接结束
        Druid获取连接数据库对象过程
            1)导包
            2)配置文件--严格按照Druid要求书写
            3)创建属性列表集合
                Properties
            4)读取配置文件
            5)将读取的字节输入流文件加载到属性列表中
            6)从连接池获取对象
                com.alibaba.druid.pool.DruidDataSourceFactory工厂类提供了方法
                    --public static DataSource createDataSource(配置文件名)
                        --创建数据源DataSource数据源接口对象
                    本质
                        DruidDataSource实现了DataSource接口
    1. import com.alibaba.druid.pool.DruidDataSourceFactory;
    2. import javax.sql.DataSource;
    3. import java.io.InputStream;
    4. import java.sql.Connection;
    5. import java.util.Properties;
    6. //利用Druid连接池获取数据库连接对象
    7. public class druidTest {
    8. public static void main(String[] args) throws Exception {
    9. //导包
    10. //书写配置文件
    11. //创建属性列表集合
    12. Properties prop = new Properties();
    13. //读取配置文件中的内容
    14. InputStream input = druidTest.class.getClassLoader()
    15. .getResourceAsStream("druid.properties");
    16. //将读取的内容加载到属性列表集合中
    17. prop.load(input);
    18. //利用DruidDataSourceFactory类方法实现DataSource接口
    19. DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
    20. //利用DataSource中方法getConnection()获取数据库连接对象
    21. Connection connection = dataSource.getConnection();
    22. //输出查看结果
    23. System.out.println(connection);
    24. /*
    25. 十二月 06, 2022 8:44:24 下午 com.alibaba.druid.pool.DruidDataSource info
    26. 信息: {dataSource-1} inited
    27. com.mysql.jdbc.JDBC4Connection@75412c2f
    28. */
    29. //释放资源,归还连接池
    30. connection.close();
    31. }
    32. }

    4. ThreadLocal

    ThreadLocal--线程变量--与连接池连用,保护数据安全!
        隔离线程
            synchronized
                --使多个线程安全的抢占同一资源--数据共享
            ThreadLocal
                --使每个线程都使用自己的资源--数据隔离
        格式
            ThreadLocal<存储类型> 名 = new ThreadLocal<>()
        方法
            public T get()
                --获取当前线程中执行的值
            public void set(T value)
                --将任意内容绑定当前线程,不为空则更新,为空则直接赋值
            public void remove()
                --线程使用完毕之后,需要将内容从中解绑--否则可能会造成数据泄露

    5. 优化Utils工具类-Druid-ThreadLocal

    1. import com.alibaba.druid.pool.DruidDataSourceFactory;
    2. import javax.sql.DataSource;
    3. import java.io.IOException;
    4. import java.io.InputStream;
    5. import java.sql.Connection;
    6. import java.sql.PreparedStatement;
    7. import java.sql.ResultSet;
    8. import java.sql.SQLException;
    9. import java.util.Properties;
    10. //优化工具类,加入Druid和ThreadLocal
    11. public class DruidUtils {
    12. //声明DataSource变量
    13. private static DataSource dataSource = null ;
    14. //声明ThreadLocal对象--存储连接对象,所以泛型存储Connection
    15. private static ThreadLocal threadLocal = new ThreadLocal<>();
    16. //无参构造私有--外界不能创建实例
    17. private DruidUtils(){}
    18. //书写静态代码块,类加载就读取配置文件,完成数据库连接以及获取对象
    19. static {
    20. try {
    21. //创建属性集合列表
    22. Properties properties = new Properties();
    23. //读取配置文件
    24. InputStream inputStream = DruidUtils.class.getClassLoader()
    25. .getResourceAsStream("druid.properties");
    26. //将读取结果加载进列表
    27. properties.load(inputStream);
    28. //通过DruidDataSourceFactory类的方法获取DataSource对象
    29. dataSource = DruidDataSourceFactory.createDataSource(properties);
    30. } catch (IOException e) {
    31. e.printStackTrace();
    32. } catch (Exception e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. //创建公共的获取数据源方法--后面有用
    37. public static DataSource getDataSource(){
    38. return dataSource ;
    39. }
    40. //创建公共的获取数据库链接对象的安全方法!--ThreadLocal
    41. public static Connection getConnection() {
    42. Connection connection = null ;
    43. //通过ThreadLocal获取一个线程
    44. connection = threadLocal.get() ;//因为存储的是Connection,所以取出也是
    45. //判断取出内容是否为空,是否占用已有内容线程
    46. if (connection==null){
    47. //当前线程没有连接对象,从连接池中取出一个绑定在一起
    48. try {
    49. connection = dataSource.getConnection();
    50. threadlocal.set(connection) ;
    51. return connection ;
    52. } catch (SQLException throwables) {
    53. throwables.printStackTrace();
    54. }
    55. }
    56. return null ;
    57. }
    58. //JDBC控制事务
    59. //手动开启事务
    60. public static void controlTransaction(){
    61. try {
    62. Connection connection = getConnection();
    63. connection.setAutoCommit(false);
    64. } catch (SQLException throwables) {
    65. throwables.printStackTrace();
    66. }
    67. }
    68. //回滚
    69. public static void rollBack(){
    70. Connection connection = getConnection() ;
    71. try {
    72. connection.rollback();
    73. connection.close(); //释放资源
    74. threadLocal.remove(); //解绑
    75. } catch (SQLException throwables) {
    76. throwables.printStackTrace();
    77. }
    78. }
    79. //提交
    80. public static void commit(){
    81. Connection connection = getConnection();
    82. try {
    83. connection.commit();
    84. connection.close();//释放资源
    85. threadLocal.remove();//解绑
    86. } catch (SQLException throwables) {
    87. throwables.printStackTrace();
    88. }
    89. }
    90. //释放资源
    91. //释放资源--增删改
    92. public static void close(PreparedStatement preparedStatement, Connection connection){
    93. if (preparedStatement!=null){
    94. try {
    95. preparedStatement.close();//释放资源
    96. } catch (SQLException throwables) {
    97. throwables.printStackTrace();
    98. }
    99. }
    100. if (connection!=null){
    101. try {
    102. connection.close();//释放资源
    103. threadLocal.remove(); //解绑
    104. } catch (SQLException throwables) {
    105. throwables.printStackTrace();
    106. }
    107. }
    108. }
    109. //释放资源--查询
    110. public static void close(ResultSet resultSet,PreparedStatement preparedStatement,Connection connection){
    111. if (resultSet!=null){
    112. try {
    113. resultSet.close();//释放资源
    114. } catch (SQLException throwables) {
    115. throwables.printStackTrace();
    116. }
    117. }
    118. if (preparedStatement!=null){
    119. try {
    120. preparedStatement.close();//释放资源
    121. } catch (SQLException throwables) {
    122. throwables.printStackTrace();
    123. }
    124. }
    125. if (connection!=null){
    126. try {
    127. connection.close();//释放资源
    128. threadLocal.remove(); //解绑
    129. } catch (SQLException throwables) {
    130. throwables.printStackTrace();
    131. }
    132. }
    133. }
    134. }

  • 相关阅读:
    linux环境验证c++程序库间调用
    【java学习】this关键字(27)
    windows和ubuntu下显式调用dll库文件的异同
    bert ranking pairwise demo
    git fetch与git pull的区别
    2023年【山东省安全员A证】考试题及山东省安全员A证免费试题
    手写方法实现字符串例如:“123“与整型例如:123相互转化(面试必会)
    Mac开发环境配置- Shell/Homebrew/ruby
    Java指令重排序在多线程环境下的应对策略
    前端关于对象中套用对象传参的小问题
  • 原文地址:https://blog.csdn.net/weixin_53118395/article/details/128212013