zoukankan      html  css  js  c++  java
  • 08第十天JDBC开发

    一、JDBC:Java DataBase Connectivity
      (一)、JDBC基础
          1、数据库驱动:
    数据库厂商为了方便开发人员从程序中操作数据库而提供的一套jar包,通过导入这个jar包就可以调用其中的方法操作数据库,这样的jar包就叫做数据库驱动。
           2、JDBC:两个包,包括java.sql和javax.sql。开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动)。
    sun定义的一套标准,本质上是一大堆的操作数据库的接口,所有数据库厂商为java设计的数据库驱动都实现过这套接口,这样一来同一了不同数据库驱动的方法,开发人员只需要学习JDBC就会使用任意数据库驱动了。
      (二)、JDBC实现
           1、新建Java项目-->新建lib文件夹-->导入mysql-connector-java-5.1.40-bin.jar-->邮右键build path -->add to build path--》新建类。               
                六个步骤实现JDBC
    1. package com.lmd.jdbc;
    2. //接口,面向接口编程,更换数据库不变,通用性
    3. import java.sql.Connection;
    4. import java.sql.DriverManager;
    5. import java.sql.ResultSet;
    6. import java.sql.SQLException;
    7. import java.sql.Statement;
    8. //mysql做的实现
    9. import com.mysql.jdbc.Driver;
    10. public class JDBCDemo1 {
    11. public static void main(String[] args) throws SQLException{
    12. //1.注册数据库驱动
    13. DriverManager.registerDriver(new Driver());
    14. //Class.forName("com.mysql.jdbc.Driver");
    15. //2.获取数据库连接
    16. String url = "jdbc:mysql://localhost:3306/day10";
    17. Connection con = DriverManager.getConnection(url, "root", "666666");
    18. //Connection con = DriverManager.getConnection(url +
    19. //"?user=root&password=666666");

    20. //Connection con = DriverManager.getConnection("jdbc:mysql:///
    21. //day10?user=root&password=666666");
    22. //3.获取传输器对象
    23. Statement st = con.createStatement();
    24. //4.利用传输器传输sql语句到数据库中执行,获取结果集对象
    25. ResultSet rs = st.executeQuery("select * from user");
    26. //5.遍历结果集获取查询结果
    27. while(rs.next()){
    28. String name = rs.getString("name");
    29. System.out.println(name);
    30. }
    31. //6.关闭资源
    32. rs.close();
    33. st.close();
    34. con.close();
    35. }
    36. }

          2、DriverManager
                 (1)、Jdbc程序中的DriverManager用于加载驱动,并创建与数据库的链接,这个API的常用方法:
                                DriverManager.registerDriver(new Driver());
                                DriverManager.getConnection(url, user, password);
     
                注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:
                   1)、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
                   2)、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。 创建mysql的Driver对象,导致程序和具体的mysql驱动绑死在一起,在切换数据库时需改动JAVA代码。
    1. package com.mysql.jdbc;
    2. import java.sql.SQLException;
    3. public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    4. // Register ourselves with the DriverManager
    5. static {
    6. try {
    7. java.sql.DriverManager.registerDriver(new Driver());
    8. } catch (SQLException E) {
    9. throw new RuntimeException("Can't register driver!");
    10. }
    11. }
    12. public Driver() throws SQLException {
    13. // Required for Class.forName().newInstance()
    14. }
    15. }

                 (2)、推荐方式:Class.forName(“com.mysql.jdbc.Driver”);
               采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
             同样,在开发中也不建议采用具体的驱动类型指向getConnection方法返回的connection对象。
          3、数据库URL
             URL用于标识数据库的位置,程序员通过URL地址告诉JDBC程序连接哪个数据库,URL的写法为:
    1. 常用数据库URL地址的写法:
    2. Oracle写法:jdbc:oracle:thin:@localhost:1521:sid
    3. SqlServerjdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
    4. MySqljdbc:mysql://localhost:3306/sid
    5. Mysqlurl地址的简写形式: jdbc:mysql:///sid
    6. 常用属性:useUnicode=true&characterEncoding=UTF-8

          4、程序详解—Connection
             Jdbc程序中的Connection,它用于代表数据库的链接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:
    1. ResultSet createStatement():创建向数据库发送sqlstatement对象。
    2. PrepareStatement prepareStatement(sql) :创建向数据库发送预编译sqlPrepareSatement对象。
    3. CallableStatement prepareCall(sql):创建执行存储过程的callableStatement对象。
    4. void setAutoCommit(boolean autoCommit):设置事务是否自动提交。
    5. void commit() :在链接上提交事务。
    6. void rollback() :在此链接上回滚事务。
          
          5、程序详解—Statement
             Jdbc程序中的Connection,它用于代表数据库的链接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:
    1. ResultSet executeQuery(String sql) :用于向数据发送查询语句。
    2. int executeUpdate(String sql):用于向数据库发送insertupdatedelete语句
    3. boolean execute(String sql):用于向数据库发送任意sql语句
    4. void addBatch(String sql) :把多条sql语句放到一个批处理中。
    5. int[] executeBatch():向数据库发送一批sql语句执行。

          6、程序详解—ResultSet
            (1)、 Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
            (2)、ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:
    1. 获取任意类型的数据
    2. getObject(int index)
    3. getObject(string columnName)
    4. 获取指定类型的数据,例如:
    5. getString(int index)
    6. getString(String columnName)
     
     
    1. ResultSet还提供了对结果集进行滚动的方法:
    2. boolean next():移动到下一行
    3. boolean previous():移动到前一行
    4. boolean absolute(int row):移动到指定行
    5. void beforeFirst():移动resultSet的最前面。
    6. void afterLast() :移动到resultSet的最后面。
    例如:
    1. 表中:zs lisi wangwu
    2. rs.next(); rs.next(); 打印:lisi
    3. rs.previous();//zs
    4. rs.absolute(3);//wangwu 第三行
    5. String name = rs.getString("name");
    6. System.out.println(name);

          7、程序详解—释放资源
            (1)、Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,
    这些对象通常是ResultSet、Statement和Connection对象。
            (2)、特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,
    极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
            (3)、为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
            若 if (rs != null)  rs.close();  加到 try...catch...中,后面要加一个 rs=null;  变成垃圾对象被回收。完整的如下:
    1. public class JDBCDemo1 {
    2. public static void main(String[] args){
    3. Connection conn = null;
    4. Statement stat = null;
    5. ResultSet rs = null;
    6. try {
    7. Class.forName("com.mysql.jdbc.Driver");
    8. String url = "jdbc:mysql://localhost:3306/day10";
    9. conn = DriverManager.getConnection(url, "root", "666666");
    10. stat = conn.createStatement();
    11. rs = stat.executeQuery("select * from user");
    12. while(rs.next()){
    13. String name = rs.getString("name");
    14. System.out.println(name);
    15. }
    16. } catch (Exception e) {
    17. e.printStackTrace();
    18. } finally {
    19. if (rs != null) {
    20. try {
    21. rs.close();
    22. } catch (SQLException e) {
    23. e.printStackTrace();
    24. }finally {
    25. rs = null;
    26. }
    27. }
    28. if (stat != null) {
    29. try {
    30. stat.close();
    31. } catch (SQLException e) {
    32. e.printStackTrace();
    33. }finally {
    34. stat = null;
    35. }
    36. }
    37. if (conn != null) {
    38. try {
    39. conn.close();
    40. } catch (SQLException e) {
    41. e.printStackTrace();
    42. }finally {
    43. conn = null;
    44. }
    45. }
    46. }
    47. }
    48. }
      (三)、JDBC增删改查
    1. create database day10 character set utf8 collate utf8_general_ci;
    2. use day10;
    3. create table user(
    4. id int primary key auto_increment,
    5. name varchar(40),
    6. password varchar(40),
    7. email varchar(60),
    8. birthday date
    9. )character set utf8 collate utf8_general_ci;
    10. insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04');
    11. insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04');
    12. insert into user(name,password,email,birthday) values('wangwu','123456','wangwu@sina.com','1979-12-04');
    1. mysql> select * from user;
    2. +----+--------+----------+-----------------+------------+
    3. | id | name | password | email | birthday |
    4. +----+--------+----------+-----------------+------------+
    5. | 1 | zs | 123456 | zs@sina.com | 1980-12-04 |
    6. | 2 | lisi | 123456 | lisi@sina.com | 1981-12-04 |
    7. | 3 | wangwu | 123456 | wangwu@sina.com | 1979-12-04 |
    8. +----+--------+----------+-----------------+------------+
    1. package com.lmd.util;
    2. import java.io.FileNotFoundException/FileReader;
    3. import java.sql.Connection/DriverManager/ResultSet;
    4. import java.sql.SQLException/Statement;
    5. import java.util.Properties;
    6. public class JDBCUtils {
    7. private static Properties prop = null;
    8. private JDBCUtils(){ }
    9. //只解析一次,放在静态代码块里
    10. static{
    11. prop = new Properties();
    12. try {
    13. prop.load(new FileReader(JDBCUtils.class.getClassLoader().
    14. getResource("config.properties").getPath()));
    15. } catch (FileNotFoundException e) {
    16. e.printStackTrace();
    17. } catch (Exception e) {
    18. e.printStackTrace();
    19. throw new RuntimeException(e);
    20. }
    21. }
    22. /**
    23. * 获取连接
    24. * @return
    25. * @throws ClassNotFoundException
    26. * @throws SQLException
    27. */
    28. public static Connection getConn() throws ClassNotFoundException, SQLException{
    29. //1、注册数据库驱动
    30. Class.forName(prop.getProperty("driver"));
    31. //2、获取连接
    32. return DriverManager.getConnection(prop.getProperty("url"),
    33. prop.getProperty("username"), prop.getProperty("password"));
    34. }
    35. /**
    36. * 关闭连接
    37. * @param rs
    38. * @param stat
    39. * @param conn
    40. */
    41. public static void close(ResultSet rs, Statement stat, Connection conn) {
    42. if (rs != null) {
    43. try {
    44. rs.close();
    45. } catch (SQLException e) {
    46. e.printStackTrace();
    47. }finally {
    48. rs = null;
    49. }
    50. }
    51. if (stat != null) {
    52. try {
    53. stat.close();
    54. } catch (SQLException e) {
    55. e.printStackTrace();
    56. }finally {
    57. stat = null;
    58. }
    59. }
    60. if (conn != null) {
    61. try {
    62. conn.close();
    63. } catch (SQLException e) {
    64. e.printStackTrace();
    65. }finally {
    66. conn = null;
    67. }
    68. }
    69. }
    70. }
    1. 配置文件:config.properties 在src下
    2. driver=com.mysql.jdbc.Driver
    3. url=jdbc:mysql://localhost:3306/day10
    4. username=root
    5. password=66666

          1、使用executeUpdate(String sql)方法完成数据添加操作,示例操作:
    1. @Test
    2. public void add(){
    3. Connection conn = null;
    4. Statement stat = null;
    5. try {
    6. //1、注册数据库驱动;2、获取数据库连接
    7. conn = JDBCUtils.getConn();
    8. //3、获取传输器对象
    9. stat = conn.createStatement();
    10. //4、利用传输器传输sql语句到数据库中执行,获取结果集对象
    11. int count = stat.executeUpdate("insert into user values(null,'lily','666666'
    12. ,'lily8@163.com','1990-12-22')");
    13. //5、处理结果
    14. if (count>0) {
    15. System.out.println("执行成功!影响到的行数为"+count);
    16. } else {
    17. System.out.println("执行失败!");
    18. }
    19. } catch (Exception e) {
    20. e.printStackTrace();
    21. } finally {
    22. //6、关闭连接
    23. JDBCUtils.close(null, stat, conn);
    24. }
    25. }
    结果如下:
     
          2、使用executeUpdate(String sql)方法完成数据修改操作,示例操作:
    1. public void update(){
    2. Connection conn = null;
    3. Statement stat = null;
    4. try {
    5. conn = JDBCUtils.getConn();
    6. stat = conn.createStatement();
    7. stat.executeUpdate("update user set password='888888' where id=2");
    8. } catch (Exception e) {
    9. e.printStackTrace();
    10. } finally {
    11. //6、关闭连接
    12. JDBCUtils.close(null, stat, conn);
    13. }
    14. }
    结果如下:
     
          3、使用executeQuery(String sql)方法完成数据查询操作,示例操作:
    1. @Test
    2. public void find(){
    3. Connection conn = null;
    4. Statement stat = null;
    5. ResultSet rs = null;
    6. try {
    7. conn = JDBCUtils.getConn();
    8. stat = conn.createStatement();
    9. rs = stat.executeQuery("select * from user where name='lily'");
    10. while (rs.next()) {
    11. String name = rs.getString("name");
    12. String password = rs.getString("password");
    13. System.out.println(name+":"+password);
    14. }
    15. } catch (Exception e) {
    16. e.printStackTrace();
    17. } finally {
    18. //6、关闭连接
    19. JDBCUtils.close(null, stat, conn);
    20. }
    21. }
    结果如下:

          4、使用 executeUpdate(String sql)方法完成数据删除操作,示例操作:
    1. @Test
    2. public void delete(){
    3. Connection conn = null;
    4. Statement stat = null;
    5. try {
    6. conn = JDBCUtils.getConn();
    7. stat = conn.createStatement();
    8. stat.executeUpdate("delete from user where name='lily'");
    9. } catch (Exception e) {
    10. e.printStackTrace();
    11. } finally {
    12. //6、关闭连接
    13. JDBCUtils.close(null, stat, conn);
    14. }
    15. }
    结果如下:
       (四)、案例:改写前面讲解的用户注册和登陆案例,实现如下需求:
    1. 1、把xml换成数据库,重写XmlUserDao
    2. 2、定义DAO接口,并定义Dao工厂,实现service层和dao层的解耦。
    3. 3、自定义dao异常。
    4. 4、防范sql注入攻击
           
    1. 一、为什么:要分层
    2. 使软件具有结构性,便于开发、维护和管理。
    3. 将不同功能模块独立,在需要替换某一模块时不需要改动其他模块,方便代码的复用、替换
    4. 二、层与层耦合的概念,利用工厂类解耦
    5. 在分层结构中,我们希望将各个功能
    6. 约束在各自的模块(层)当中的,而当属于某一层的对象、方法“入侵”到了其他层,如将web层的ServletContext对象
    7. 传入service层,或service层调用XMLDao独有的方法,就会导致层与层之间的关系过于“紧密”,当需要修改某一层时不可
    8. 避免的要修改其他关联的层,这和我们软件分层最初的设想-----层与层分离,一个层尽量不依赖其他层存在,
    9. 当修改一层时无需修改另一层的设想是违背的。这种“入侵”造成的“紧密”关系就早做层与层之间发生的“耦合”,
    10. 而去掉这种耦合性的过程就叫做层与层之间“解耦”
    11. 利用工厂类可以实现解耦的功能
    12. 三、如何判断一项功能到底属于哪一层
    13. 某一项功能属于哪一层,往往是不能明确确定出来的,这时可以参考如下标准进行判断:
    14. 1、此项功能在业务逻辑上更贴近与哪一层,放在哪一层更能较少耦合
    15. 2、此项功能是否必须使用某一层特有的对象
    16. 3、如果放在哪一层都可以,那么放在哪一层更方便技术上的实现,及方便代码的编写和维护
    17. 四、异常的处理
    18. 1、如果一个异常抛给上一层会增加程序的耦合性,请当场解决:如将xml解析错误抛给service层,那么当换成mysqldao时,
    19. 还需要修改service去掉xml解析异常的处理。
    20. 2、如果上一层明确需要此异常进行代码的流转,请抛出:如当查找一个用户信息而用户找不到时,
    21. 可以抛出一个用户找不到异常,明确要求上一层处理。
    22. 3、如果这一层和上一层都能解决尽量在这一层解决掉。
    23. 4、如果这一层不能解决,而上一层能解决抛给上一层。
    24. 5、如果所有层都不能解决,则应抛出给虚拟机使线程停止,但是如果直接抛出这个异常,则还需要调用者一级一级继续
    25. 往上抛出最后才能抛给虚拟机,所以还不如在出现异常的位置直接try...catch住后转换为RuntimeException抛出。
    26. :如读取配置文件出错,任何层都不能解决,转为RuntimeException抛出,停止线程。

    1、改写案例
    (1)、把xml换成数据库,重写XmlUserDao
    1. create table users(
    2. id int primary key auto_increment,
    3. username varchar(20),
    4. password varchar(50),
    5. nickname varchar(40),
    6. email varchar(50)
    7. );
    8. insert into users values(null,'admin','admin','admin','admin8@qq.com');
      (2)、重写XmlUserDao,改为MysqlUserDao.java。
    1)、将XmlUserDao中的方法定义到一个接口中,注意:
      使用接口定义使用的三个方法;接口一定要有注释,并且是文档注释。
    方法注释一方都放在接口中,而非实现类中。
    在接口中使用接口注释后,当鼠标移到实现类中方法上就会有提示。
    1. package com.lmd.dao;
    2. import com.lmd.domain.User;
    3. public interface UserDao {
    4. /**
    5. * 根据用户名查找用户
    6. * @param username 用户名
    7. * @return 根据用户名找到的用户信息bean,若没找到返回null
    8. */
    9. public User findUserByUserName(String username);
    10. /**
    11. * 添加用户
    12. * @param user 要添加的用户信息bean
    13. */
    14. public void addUser(User user);
    15. /**
    16. * 根据用户名和密码查找对应的用户
    17. * @param username 用户名
    18. * @param password 密码
    19. * @return 找到的用户,若没找到返回null
    20. */
    21. public User findUserByUNandPSW(String username, String password);
    22. }
    2)、仍需要前面设置的config.properties和JDBCUtils.java,开发MysqlUserDao.java:
                   修改User.java,增加id;修改UserService.java一句话。
    1. //private XmlUserDao dao = new XmlUserDao();
    2. private MysqlUserDao dao = new MysqlUserDao();
    这个切换数据库会改变代码,不太好,后面会讲怎么办? 这是一种耦合,后面会利用工厂类实现解耦
    1. package com.lmd.dao;
    2. import java.sql.Connection;
    3. import java.sql.ResultSet;
    4. import java.sql.Statement;
    5. import com.lmd.domain.User;
    6. import com.lmd.util.JDBCUtils;
    7. public class MysqlUserDao implements UserDao{
    8. @Override
    9. public User findUserByUserName(String username) {
    10. String sql = "select * from users where username='"+username+"'";
    11. Connection conn = null;
    12. Statement stat = null;
    13. ResultSet rs = null;
    14. try {
    15. conn = JDBCUtils.getConn();
    16. stat = conn.createStatement();
    17. rs = stat.executeQuery(sql);
    18. if (rs.next()) {
    19. User user = new User();
    20. user.setId(rs.getInt("id"));
    21. user.setUsername(rs.getString("username"));
    22. user.setPassword(rs.getString("password"));
    23. user.setNickname(rs.getString("nickname"));
    24. user.setEmail(rs.getString("email"));
    25. return user;
    26. } else {
    27. return null;
    28. }
    29. } catch (Exception e) {
    30. e.printStackTrace();
    31. throw new RuntimeException(e);
    32. } finally {
    33. JDBCUtils.close(rs, stat, conn);
    34. }
    35. }
    36. @Override
    37. public void addUser(User user) {
    38. String sql = "insert into users values (null,'"+user.getUsername()+
    39. "','"+user.getPassword()+"','"+user.getNickname()+"','"+user.getEmail()+"')";
    40. Connection conn = null;
    41. Statement stat = null;
    42. try {
    43. conn = JDBCUtils.getConn();
    44. stat = conn.createStatement();
    45. stat.executeUpdate(sql);
    46. } catch (Exception e) {
    47. e.printStackTrace();
    48. throw new RuntimeException(e);
    49. } finally {
    50. JDBCUtils.close(null, stat, conn);
    51. }
    52. }
    53. @Override
    54. public User findUserByUNandPSW(String username, String password) {
    55. String sql = "select * from users where username='"+username+"' and password='"+password+"'";
    56. Connection conn = null;
    57. Statement stat = null;
    58. ResultSet rs = null;
    59. try {
    60. conn = JDBCUtils.getConn();
    61. stat = conn.createStatement();
    62. rs = stat.executeQuery(sql);
    63. if (rs.next()) {
    64. User user = new User();
    65. user.setId(rs.getInt("id"));
    66. user.setUsername(rs.getString("username"));
    67. user.setPassword(rs.getString("password"));
    68. user.setNickname(rs.getString("nickname"));
    69. user.setEmail(rs.getString("email"));
    70. return user;
    71. } else {
    72. return null;
    73. }
    74. } catch (Exception e) {
    75. e.printStackTrace();
    76. throw new RuntimeException(e);
    77. } finally {
    78. JDBCUtils.close(rs, stat, conn);
    79. }
    80. }
    81. }

             2、定义DAO接口,并定义Dao工厂,实现service层和dao层的解耦。
    1. package com.lmd.factory;
    2. import java.io.FileReader;
    3. import java.util.Properties;
    4. import com.lmd.dao.UserDao;
    5. /**
    6. * 单例模式:保证在整个应用程序的生命周期中,
    7. * 任何一个时刻,单例类的实例只存在一个(也可以不存在)
    8. * @author angel11288
    9. *
    10. */
    11. public class DaoFactory {
    12. private static DaoFactory factory = new DaoFactory();
    13. //配置文件只读一次
    14. private static Properties prop = null;
    15. static{
    16. try {
    17. prop = new Properties();
    18. prop.load(new FileReader(DaoFactory.class.getClassLoader().
    19. getResource("config.properties").getPath()));
    20. } catch (Exception e) {
    21. e.printStackTrace();
    22. throw new RuntimeException(e);
    23. }
    24. }
    25. private DaoFactory() {}
    26. public static DaoFactory getFactory() {
    27. return factory;
    28. }
    29. public UserDao getDao() {
    30. try {
    31. String cla = prop.getProperty("UserDao");//com.lmd.dao.MysqlUserDao
    32. return (UserDao)Class.forName(cla).newInstance();
    33. } catch (Exception e) {
    34. e.printStackTrace();
    35. throw new RuntimeException(e);
    36. }
    37. }
    38. }
    修改UserService:
    1. //private XmlUserDao dao = new XmlUserDao();
    2. //private MysqlUserDao dao = new MysqlUserDao();
    3. private UserDao dao = DaoFactory.getFactory().getDao();
    配置文件config.properties:
    1. driver=com.mysql.jdbc.Driver
    2. url=jdbc:mysql://localhost:3306/day10
    3. username=root
    4. password=666666
    5. UserDao=com.lmd.dao.MysqlUserDao
    6. #UserDao=com.lmd.dao.XmlUserDao
             3、自定义dao异常。
           4、SQL注入攻击:定义DAO接口,并定义Dao工厂,实现service层和dao层的解耦。
    (1)、由于dao中执行的SQL语句是拼接出来的,其中有一部分内容是由用户从客户端传入,所以当用户传入的数据中包含sql关键字时,就有可能通过这些关键字改变sql语句的语义,从而执行一些特殊的操作,这样的攻击方式就叫做sql注入攻击。  (登陆之后,注销,再次回来,只有用户名也可以登录)
    admin 'or' 1=1,密码空,也可以登录成功。
    (2)、PreperedStatement是Statement的孩子,它的实例对象可以通过调用Connection.preparedStatement()
    方法获得,相对于Statement对象而言:
    1)、PreperedStatement可以避免SQL注入的问题。
    2)、Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
    PreparedStatement 可对SQL进行预编译,从而提高数据库的执行效率。
    3)、并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
    PreparedStatement利用预编译的机制将sql语句的主干和参数分别传输给数据库服务器,从而使数据库分辨
    的出哪些是sql语句的主干哪些是参数,这样一来即使参数中带了sql的关键字,数据库服务器也仅仅将他当
    作参数值使用,关键字不会起作用,从而从原理上防止了sql注入的问题
    1. package com.lmd.jdbc;
    2. import java.sql.Connection;
    3. import java.sql.ResultSet;
    4. import java.sql.PreparedStatement;
    5. import com.lmd.util.JDBCUtils;
    6. public class JDBCDemo3 {
    7. public static void main(String[] args) {
    8. Connection conn = null;
    9. PreparedStatement ps = null;
    10. ResultSet rs = null;
    11. try {
    12. conn = JDBCUtils.getConn();
    13. //主干语句
    14. ps = conn.prepareStatement("select * from user where name=? and password=?");
    15. ps.setString(1, "lisi");
    16. ps.setString(2, "888888");
    17. rs = ps.executeQuery();
    18. while (rs.next()) {
    19. System.out.println(rs.getString("email"));
    20. }
    21. } catch (Exception e) {
    22. e.printStackTrace();
    23. } finally {
    24. JDBCUtils.close(rs, ps, conn);
    25. }
    26. }
    27. }

    (3)、PreparedStatement主要有如下的三个优点:
    ~1.可以防止sql注入
    ~2.由于使用了预编译机制,执行的效率要高于Statement。
    ~3.sql语句使用?形式替代参数,然后再用方法设置?的值,比起拼接字符串,代码更加优雅。
    二、JDBC应用
    (一)、使用JDBC处理大数据
            1、基础:
             (1)、在实际开发中,程序需要把大文本 Text 或二进制数据 Blob保存到数据库。Text是mysql叫法,Oracle中叫Clob。
             (2)、基本概念:大数据也称之为LOB(Large Objects),LOB又分为:clob和blob
                         1)、clob用于存储大文本。Text
                         2)、blob用于存储二进制数据,例如图像、声音、二进制文等。
             (3)、对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
      1)、Text:TINYTEXT(255)、TEXT(64k)、MEDIUMTEXT(16M)和LONGTEXT(4G)
                         2)、Blob:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
            2、使用JDBC处理大文本:
             (1)、对于MySQL中的Text类型,可调用如下方法设置:
    1. PreparedStatement.setCharacterStream(index, reader, length);
    2. //注意length长度须设置,并且设置为int型
    3. //当包过大时修改配置:[mysqld] max_allowed_packet=64M

             (2)、对MySQL中的Text类型,可调用如下方法获取:
    1. reader = resultSet. getCharacterStream(i);
    2. 等价于
    3. reader = resultSet.getClob(i).getCharacterStream()
    1. package com.lmd.job;
    2. /*
    3. * create table textdemo(
    4. * id int primary key auto_increment,
    5. * name varchar(20),
    6. * content MEDIUMTEXT
    7. * );
    8. */
    9. import java.io.File/FileReader;
    10. import java.sql.Connection/PreparedStatement/ResultSet;
    11. import org.junit.Test;
    12. import com.lmd.util.JDBCUtils;
    13. public class TextDemo1 {
    14. @Test
    15. public void addText() {
    16. Connection conn = null;
    17. PreparedStatement ps = null;
    18. ResultSet rs = null;
    19. try{
    20. conn = JDBCUtils.getConn();
    21. ps = conn.prepareStatement("insert into textdemo values (null, ?,?)");
    22. ps.setString(1, "三生三世十里桃花.txt");
    23. File file = new File("三生三世十里桃花.txt");
    24. ps.setCharacterStream(2, new FileReader(file), file.length());
    25. ps.executeUpdate();
    26. } catch(Exception e){
    27. e.printStackTrace();
    28. } finally {
    29. JDBCUtils.close(rs, ps, conn);
    30. }
    31. }
    32. }
    修改java虚拟机启动内存大小:
    查看安装目录下的myeclipse.ini:
    1. #utf8 (do not remove)
    2. -startup
    3. plugins/org.eclipse.equinox.launcher_1.3.100.v20150511-1540.jar
    4. --launcher.library
    5. plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.300.v20150602-1417
    6. -vm
    7. D:Program FilesMyEclipse2016inarycom.sun.java.jdk8.win32.x86_64_1.8.0.u66injavaw.exe
    8. -configuration
    9. D:Program FilesMyEclipse2016configuration
    10. -install
    11. D:Program FilesMyEclipse2016
    12. -vmargs
    13. -Xmx1024m
    14. -XX:ReservedCodeCacheSize=64m
    15. -Dosgi.nls.warnings=ignore
    异常处理
    1. 1.Exception in thread "main" java.lang.AbstractMethodError: com.mysql.jdbc.PreparedStatement.setCharacterStream
    2. (ILjava/io/Reader;J)Vps.setCharacterStream(2, new FileReader(file), file.length());
    3. 第三个参数是long型的是从1.6才开始支持的,驱动里还没有开始支持。
    4. 解决方案:ps.setCharacterStream(2, new FileReader(file), (int)file.length());
    5. 2.Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    6. 文件大小过大,导致PreparedStatement中数据多大占用内存,内存溢出
    7. -Xms256M-Xmx256M
    8. 3.com.mysql.jdbc.PacketTooBigException: Packet for query is too large (10886466 > 1048576).
    9. You can change this value on the server by setting the max_allowed_packet' variable.
    10. 数据库连接传输用的包不够大,传输大文本时报此错误
    11. 在my.ini中配置max_allowed_packet指定包的大小
    1、修改java虚拟机启动内存大小:
    CMD修改:java -Xms 64M -Xmx 128M
    MyEclipse中Run-->Run config...-->Arugument-->VMargument:-Xms64m 换行 -Xmx 128m
    2、com.mysql.jdbc.PacketTooBigException: Packet for query is too large (6882307 > 4194304). 
    You can change this value on the server by setting the max_allowed_packet' variable.
    修改C:ProgramDataMySQLMySQL Server 5.6下的my.ini:在[msqld]中写
    1. public void findText() {
    2. Connection conn = null;
    3. PreparedStatement ps = null;
    4. ResultSet rs = null;
    5. try{
    6. conn = JDBCUtils.getConn();
    7. ps = conn.prepareStatement("select * from textdemo where id=?");
    8. ps.setInt(1,2);
    9. rs = ps.executeQuery();
    10. while (rs.next()) {
    11. String name = rs.getString("name");
    12. System.out.println(name);//三生三世十里桃花.txt
    13. Reader reader = rs.getCharacterStream("content");
    14. Writer writer = new FileWriter(name);
    15. char[] chs = new char[1024];
    16. int len = 0;
    17. while ((len = reader.read(chs))!=-1) {
    18. writer.write(chs, 0, len);
    19. }
    20. reader.close();
    21. writer.close();
    22. }
    23. } catch(Exception e){
    24. e.printStackTrace();
    25. } finally {
    26. JDBCUtils.close(rs, ps, conn);
    27. }
    28. }

     3、使用JDBC处理二进制数据:
             (1)、对于MySQL中的BLOB类型,可调用如下方法设置:
    1. PreparedStatement. setBinaryStream(i, inputStream, length);

             (2)、对MySQL中的BLOB类型,可调用如下方法获取:
    1. InputStream in = resultSet.getBinaryStream(i);
    2. InputStream in = resultSet.getBlob(i).getBinaryStream();

    1. @Test
    2. public void addBlob() {
    3. try{
    4. conn = JDBCUtils.getConn();
    5. ps = conn.prepareStatement("insert into blobdemo values (null,?,?)");
    6. ps.setString(1, "欢迎您.mp3");
    7. File file = new File("1.mp3");
    8. ps.setBinaryStream(2, new FileInputStream(file), file.length());
    9. ps.executeUpdate();
    10. }
    11. }
    12. @Test
    13. public void findBlob() {
    14. try{
    15. conn = JDBCUtils.getConn();
    16. ps = conn.prepareStatement("select * from blobdemo where id=?");
    17. ps.setInt(1,1);
    18. rs = ps.executeQuery();
    19. while (rs.next()) {
    20. String name = rs.getString("name");
    21. System.out.println(name);//欢迎您.mp3
    22. InputStream input = rs.getBinaryStream("content");
    23. OutputStream output = new FileOutputStream(name);
    24. byte[] bys = new byte[1024];
    25. int len = 0;
    26. while ((len = input.read(bys))!=-1) {
    27. output.write(bys, 0, len);
    28. }
    29. input.close();
    30. output.close();
    31. }
    32. }
    但是:图片和音乐等一般不会存在数据库中,只需要知道就行。大文件一般放在服务器的硬盘上,访问的是服务器存在的
    文件的硬盘路径,通过读路径得到大文件。

     3、使用JDBC进行批处理:
             (1)、业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的
    批处理机制,以提升执行效率。

             (2)、实现批处理有两种方式,第一种方式:
                         1)、void  Statement.addBatch(sql) 。
                         2)、执行批处理SQL语句。
                         3)、int[]  executeBatch()方法:执行批处理命令。返回包含批中每个命令的一个元素的更新计数所组成的数组。
                         4)、void clearBatch()方法:清除批处理命令 。
                  采用Statement.addBatch(sql)方式实现批处理:
                     优点:可以向数据库发送多条不同的SQL语句。
                     缺点SQL语句没有预编译。当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。
    1. package com.lmd.batch;
    2. import java.sql.Connection;
    3. import java.sql.ResultSet;
    4. import java.sql.Statement;
    5. import com.lmd.util.JDBCUtils;
    6. public class StatementBatch {
    7. public static void main(String[] args) {
    8. Connection conn = null;
    9. Statement stat = null;
    10. try {
    11. conn = JDBCUtils.getConn();
    12. stat = conn.createStatement();
    13. stat.addBatch("create database day10batch");
    14. stat.addBatch("use day10batch");
    15. stat.addBatch("create table batchdemo("
    16. +"id int primary key auto_increment,"
    17. + "name varchar(20))");
    18. stat.addBatch("insert into batchdemo values(null,'aaa')");
    19. stat.addBatch("insert into batchdemo values(null,'bbb')");
    20. stat.addBatch("insert into batchdemo values(null,'ccc')");
    21. stat.addBatch("insert into batchdemo values(null,'ddd')");
    22. stat.executeBatch();//返回int类型的数组
    23. } catch (Exception e) {
    24. e.printStackTrace();
    25. } finally {
    26. JDBCUtils.close(null, stat, conn);
    27. }
    28. }
    29. }
    结果如下:
    1. mysql> use day10batch;
    2. Database changed
    3. mysql> select * from batchdemo;
    4. +----+------+
    5. | id | name |
    6. +----+------+
    7. | 1 | aaa |
    8. | 2 | bbb |
    9. | 3 | ccc |
    10. | 4 | ddd |
    11. +----+------+
    12. 4 rows in set
    13. mysql>


            (3)、实现批处理有两种方式,第一种方式: PreparedStatement.addBatch()
                   采用PreparedStatement.addBatch()实现批处理,将一组参数添加到此 PreparedStatement 对象的批处理命令中。
                       优点:发送的是预编译后的SQL语句,执行效率高。
                       缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
    1. package com.lmd.batch;
    2. import java.sql.Connection;
    3. import java.sql.PreparedStatement;
    4. import com.lmd.util.JDBCUtils;
    5. /*
    6. create table psbatch(
    7. id int primary key auto_increment,
    8. name varchar(20)
    9. );
    10. */
    11. public class PreparedStatementBatch {
    12. public static void main(String[] args) {
    13. Connection conn = null;
    14. PreparedStatement ps = null;
    15. try {
    16. conn = JDBCUtils.getConn();
    17. ps = conn.prepareStatement("insert into psbatch values(null,?)");
    18. for (int i = 0; i < 100001; i++) {
    19. ps.setString(1, "name"+i);
    20. ps.addBatch();
    21. if (i%1000==0) {
    22. ps.executeBatch();
    23. ps.clearBatch();
    24. }
    25. }
    26. ps.executeBatch();//为了剩下的那一条记录
    27. } catch (Exception e) {
    28. e.printStackTrace();
    29. } finally {
    30. JDBCUtils.close(null, ps, conn);
    31. }
    32. }
    33. }
    结果如下:
    1. mysql> select count(*) from psbatch;
    2. +----------+
    3. | count(*) |
    4. +----------+
    5. | 419 |
    6. +----------+
    7. 1 row in set
    8. mysql> select count(*) from psbatch;
    9. +----------+
    10. | count(*) |
    11. +----------+
    12. | 689 |
    13. +----------+
    14. 1 row in set
    15. mysql>



  • 相关阅读:
    设置内存管理
    Kill Session
    设置In_Memery
    查询无效对象 及 重新编译
    Oracle 硬解析查询
    设置Oracle 12C OEM 端口
    创建Mysql 序列
    compress 表设置及索引设置
    闪回表
    ECS Samples概述
  • 原文地址:https://www.cnblogs.com/angel11288/p/28ea9cd81e4e2665d1a778f7e14e16d5.html
Copyright © 2011-2022 走看看