一、操作和访问数据库

在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:

  • Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
  • PreparedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
  • CallableStatement:用于执行 SQL 存储过程

image.png

二、使用Statement操作数据表的弊端

  • 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
  • Statement 接口中定义了下列方法用于执行 SQL 语句:

    1. int excuteUpdate(String sql):执行更新操作INSERTUPDATEDELETE
    2. ResultSet executeQuery(String sql):执行查询操作SELECT
  • 但是使用Statement操作数据表存在弊端:

    • 问题一:存在拼串操作,繁琐
    • 问题二:存在SQL注入问题
  • SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=’a’ OR 1 = ‘ AND password = ‘ OR ‘1’ =’1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法。
  • 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。

    1. public class StatementTest {
    2. // 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
    3. //如何避免出现sql注入:只要用 PreparedStatement(从Statement扩展而来) 取代 Statement
    4. @Test
    5. public void testLogin() {
    6. Scanner scanner = new Scanner(System.in);
    7. System.out.print("请输入用户名:");
    8. String user = scanner.nextLine();
    9. System.out.print("请输入密码:");
    10. String password = scanner.nextLine();
    11. //SELECT user,password FROM user_table WHERE user = '1' or ' AND password = '=1 or '1' = '1'
    12. String sql = "SELECT user,password FROM user_table WHERE user = '"+ user +"' AND password = '"+ password +"'";
    13. User returnUser = get(sql,User.class);
    14. if(returnUser != null){
    15. System.out.println("登录成功");
    16. }else{
    17. System.out.println("用户名不存在或密码错误");
    18. }
    19. }
    20. // 使用Statement实现对数据表的查询操作
    21. public <T> T get(String sql, Class<T> clazz) {
    22. T t = null;
    23. Connection conn = null;
    24. Statement st = null;
    25. ResultSet rs = null;
    26. try {
    27. // 1.加载配置文件
    28. InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
    29. Properties pros = new Properties();
    30. pros.load(is);
    31. // 2.读取配置信息
    32. String user = pros.getProperty("user");
    33. String password = pros.getProperty("password");
    34. String url = pros.getProperty("url");
    35. String driverClass = pros.getProperty("driverClass");
    36. // 3.加载驱动
    37. Class.forName(driverClass);
    38. // 4.获取连接
    39. conn = DriverManager.getConnection(url, user, password);
    40. st = conn.createStatement();
    41. rs = st.executeQuery(sql);
    42. // 获取结果集的元数据
    43. ResultSetMetaData rsmd = rs.getMetaData();
    44. // 获取结果集的列数
    45. int columnCount = rsmd.getColumnCount();
    46. if (rs.next()) {
    47. t = clazz.newInstance();
    48. for (int i = 0; i < columnCount; i++) {
    49. // //1. 获取列的名称
    50. // String columnName = rsmd.getColumnName(i+1);
    51. // 1. 获取列的别名
    52. String columnName = rsmd.getColumnLabel(i + 1);
    53. // 2. 根据列名获取对应数据表中的数据
    54. Object columnVal = rs.getObject(columnName);
    55. // 3. 将数据表中得到的数据,封装进对象
    56. Field field = clazz.getDeclaredField(columnName);
    57. field.setAccessible(true);
    58. field.set(t, columnVal);
    59. }
    60. return t;
    61. }
    62. } catch (Exception e) {
    63. e.printStackTrace();
    64. } finally {
    65. // 关闭资源
    66. if (rs != null) {
    67. try {
    68. rs.close();
    69. } catch (SQLException e) {
    70. e.printStackTrace();
    71. }
    72. }
    73. if (st != null) {
    74. try {
    75. st.close();
    76. } catch (SQLException e) {
    77. e.printStackTrace();
    78. }
    79. }
    80. if (conn != null) {
    81. try {
    82. conn.close();
    83. } catch (SQLException e) {
    84. e.printStackTrace();
    85. }
    86. }
    87. }
    88. return null;
    89. }
    90. }

    image.png

    三、PreparedStatement的使用

    1 PreparedStatement介绍

  • 可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象

  • PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句
  • PreparedStatement 对象所代表的 SQL 语句中的参数用问号(?)来表示,调用 PreparedStatement 对象的setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1开始),第二个是设置的 SQL 语句中的参数的值

    2 PreparedStatement vs Statement

  • 代码的可读性和可维护性。

  • PreparedStatement 能最大可能提高性能:
    • DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
    • 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。(语法检查,语义检查,翻译成二进制命令,缓存)
  • PreparedStatement 可以防止 SQL 注入

    3 Java与SQL对应数据类型转换表

    image.png

    4 使用PreparedStatement实现增、删、改操作

    4.1-新增操作

    @Test
      public void testIDU(){
    
          //1.读取配置文件的4个基本信息
          //通过系统类的加载器获取配置文件,默认路径是当前类所在路径
          Connection conn = null;
          PreparedStatement ps = null;
          try {
              InputStream st = ConnectionTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
              Properties pt = new Properties();
              pt.load(st);
    
              String username = pt.getProperty("username");
              String password = pt.getProperty("password");
              String url = pt.getProperty("url");
              String driverClass = pt.getProperty("driverClassName");
              //2.加载驱动
              Class.forName(driverClass);
              //3.获取连接
              conn = DriverManager.getConnection(url, username, password);
              //4 预编译sql语句,返回PreparedStatement的实例
              String sql = "insert into t_user(id,username,password,email)values(?,?,?,?)";
              ps = conn.prepareStatement(sql);
              //5.填充占位符
              ps.setInt(1,2);
              ps.setString(2,"mguest1");
              ps.setString(3,"mguest1");
              ps.setString(4,"wer@qq.com");
              //6.执行操作
              boolean execute = ps.execute();
              System.out.println(execute);
          } catch (Exception e) {
              e.printStackTrace();
          }finally {
              //7.资源关闭
              try {
                  if (null != ps){
                      ps.close();
                  }
    
              } catch (SQLException throwables) {
                  throwables.printStackTrace();
              }
              try {
    
                  if (null != conn) {
                      conn.close();
                  }
              } catch (SQLException throwables) {
                  throwables.printStackTrace();
              }
          }
      }
    

    优化:创建数据库连接和关闭资源是经常要做的事情,我们可以抽取出来做个工具类 ```java package com.wu.web.jdbc;

import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Properties;

/**

  • @author: wuchang
  • @Date: 2021/6/28
  • @Time: 18:58
  • @BelongsProject book
  • @BelongsPackage com.wu.web.jdbc
  • @Description: 操作数据库的工具类 */ public class JDBCUtils {

    /**

    • 获取数据库连接
    • @return
    • @throws Exception */ public static Connection getConnection() throws Exception{

      //1.读取配置文件的4个基本信息 //通过系统类的加载器获取配置文件,默认路径是当前类所在路径 InputStream st = ClassLoader.getSystemClassLoader().getResourceAsStream(“jdbc.properties”); Properties pt = new Properties(); pt.load(st);

      String username = pt.getProperty(“username”); String password = pt.getProperty(“password”); String url = pt.getProperty(“url”); String driverClass = pt.getProperty(“driverClassName”); //2.加载驱动 Class.forName(driverClass); //3.获取连接 Connection conn = DriverManager.getConnection(url, username, password); return conn; } /**

    • 关闭资源
    • @param conn
    • @param ps
    • @throws SQLException */ public static void closeResource(Connection conn,PreparedStatement ps) { if (null != conn){
       try {
           conn.close();
       } catch (SQLException throwables) {
           throwables.printStackTrace();
       }
      
      } if (null != ps){
       try {
           ps.close();
       } catch (SQLException throwables) {
           throwables.printStackTrace();
       }
      
      } } /**
    • 关闭资源
    • @param conn
    • @param ps
    • @throws SQLException */ public static void closeResource(Connection conn, PreparedStatement ps, ResultSet rs) { if (null != conn){

       try {
           conn.close();
       } catch (SQLException throwables) {
           throwables.printStackTrace();
       }
      

      } if (null != ps){

       try {
           ps.close();
       } catch (SQLException throwables) {
           throwables.printStackTrace();
       }
      

      } if (null != rs){

       try {
           rs.close();
       } catch (SQLException throwables) {
           throwables.printStackTrace();
       }
      

      } } }

      <a name="WxNsV"></a>
      ### 4.2-修改操作
      ```java
      @Test
      public void testConnection1() {
       Connection conn = null;
       PreparedStatement ps = null;
      
       try {
           //修改表中的一条记录
           //1.获取数据库连接
           conn = JDBCUtils.getConnection();
           //2.预编译sql语句,返回PreparedStatement的实例
           String sql = "update t_user set username=? where id = ?";
           ps = conn.prepareStatement(sql);
           //3.填充占位符
           ps.setObject(1,"管理员账号");
           ps.setInt(2,2);
           //4.执行
           //ps.execute( ):
           //如果执行的是查询操作,有返回结果,则此方法返回true;
           //如果执行的是增、删、改操作,没有返回结果,则此方法返回false.
           ps.execute();
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           //5.资源关闭
           JDBCUtils.closeResource(conn,ps);
       }
      }
      

      4.3-配置通用的增删改

      ```java /**

    • 使用PreparedStatement测试增删改
    • sql中占位符的个数与可变形参的长度相同! */
      public void update(String sql, Object …args) {

      Connection conn = null; PreparedStatement ps = null; boolean isFailed = true; try {

       //1.获取数据库连接
       conn = JDBCUtils.getConnection();
       //2.预编译sql语句,返回PreparedStatement的实例
       ps = conn.prepareStatement(sql);
       //3.填充占位符
       for (int i = 0; i < args.length; i++){
           ps.setObject(i + 1, args[i]);
       }
       //4.执行
       //ps.execute( ):
       //如果执行的是查询操作,有返回结果,则此方法返回true;
       //如果执行的是增、删、改操作,没有返回结果,则此方法返回false.
       //可以使用ps.executeUpdate();
       isFailed = ps.execute();
      

      } catch (Exception e) {

       e.printStackTrace();
      

      } finally {

       //5.资源关闭
       JDBCUtils.closeResource(conn,ps);
      

      } return isFailed; }

      /**

    • 测试通用的增删改 */ @Test public void testUpdate() {

      //String sql = “delete from t_user where id = ?”; //String sql = “insert into t_user(id,username,password,email)values(?,?,?,?)”; //在一些mysql中一些表是数据库的关键字,比较order,这是时候执行更新会报错,要在关键字上order,这是取消关键字校验 String sql = “update t_user set username=’黄埔尚’ where id = ?”; boolean isFailed = update(sql,3); System.out.println(isFailed); } ```

      5 使用PreparedStatement实现查询操作

      封装javabean对象 ```java package com.wu.web.jdbc; /**

  • ORM编程思想(object relational mapping)
  • 一个数据表对应一个java类
  • 表中的一条记录对应java类的一个对象
  • 表中的一个字段对应java类的一个属性
  • 对于数据库的Blob字段,因为太大是无法用对象进行封装的,一般都是用文件对应 */ public class User {

    private int id; private String username; private String password;

    public int getId() {

     return id;
    

    }

    public void setId(int id) {

     this.id = id;
    

    }

    public String getUsername() {

     return username;
    

    }

    public void setUsername(String username) {

     this.username = username;
    

    }

    public String getPassword() {

     return password;
    

    }

    public void setPassword(String password) {

     this.password = password;
    

    }

    @Override public String toString() {

     return "User{" +
             "id=" + id +
             ", username='" + username + '\'' +
             ", password='" + password + '\'' +
             '}';
    

    } }

<a name="N7pRi"></a>
### 5.1-查询语句
```java
    @Test
    public void  testQuery()  {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            String sql = "select id,username,password from t_user where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setObject(1,2);
            //执行并返回结果集
            rs = ps.executeQuery();
            //处理结果集
            //rs.next()判断结果集下一条是否有数据,如果有数据返回true,并指针下移。如果没有数据返回false,指针不下移
            if (rs.next()){
                //获取当前这条数据的各个字段值
                int id = rs.getInt(1);
                String username = rs.getString(2);
                String password = rs.getString(3);
                User user = new User();
                user.setId(id);
                user.setUsername(username);
                user.setPassword(password);
                System.out.println(user.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtils.closeResource(conn, ps, rs);
        }
    }

5.2-针对单个表配置成通用查询

public User queryForUser(String sql, Object...args)  {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData md = ps.getMetaData();
            //通过ResultSetMetaData获取结果集中的列数
            int columnCount = md.getColumnCount();
            if (rs.next()){

                User user = new User();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++){
                    //获取列值
                    Object columValue = rs.getObject(i + 1);

                    //获取每个列的列名
                    String columnName = md.getColumnName(i + 1);
                    //给User对象指定的columnName属性,赋值为columValue:通过反射
                    Field field = User.class.getDeclaredField(columnName);
                    field.setAccessible(true);
                    //将指这是到当前对象的字段上
                    field.set(user,columValue);
                }
                return user;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

    @Test
    public void  testQuery()  {
        String sql = "select id,username,password from t_user where id = ?";
        User user = queryForUser(sql,2);
        System.out.println(user);
    }

5.3-针对带下划线表字段的通用查询操作

Address类

package com.wu.web.jdbc;

/**
 * ORM编程思想(object relational mapping)
 * 一个数据表对应一个java类
 * 表中的一条记录对应java类的一个对象
 * 表中的一个字段对应java类的一个属性
 * 对于数据库的Blob字段,因为太大是无法用对象进行封装的,一般都是用文件对应
 */
public class Address {
    private int id;
    private int userId;
    private String name;
    private String province;
    private String city;
    private String county;
    private int tel;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCounty() {
        return county;
    }

    public void setCounty(String county) {
        this.county = county;
    }

    public int getTel() {
        return tel;
    }

    public void setTel(int tel) {
        this.tel = tel;
    }

    @Override
    public String toString() {
        return "Address{" +
                "id=" + id +
                ", userId=" + userId +
                ", name='" + name + '\'' +
                ", province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", county='" + county + '\'' +
                ", tel=" + tel +
                '}';
    }
}

针对于表的字段名与类的属性名不相同的情况:
1-需要将列名设置别名,别名要和javabean类的字段一致
2-获取列的别名:getColumnLabel()

public Address queryForAddress(String sql, Object...args)  {

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData md = ps.getMetaData();
            //通过ResultSetMetaData获取结果集中的列数
            int columnCount = md.getColumnCount();
            if (rs.next()){
                Address address = new Address();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++){

                    //获取每个列的列值:通过ResultSet
                    Object columValue = rs.getObject(i + 1);

                    //获取每个列的列名:通过ResultSetMetaData
                    //获取列的列名:getColumnName()--不推荐使用
                    //String columnName = md.getColumnName(i + 1);
                    //2-获取列的别名:getColumnLabel()
                    String columnLabel = md.getColumnLabel(i + 1);
                    //通过反射,将对象指定名columnLabel的属性赋值为指定的值columnValue
                    Field field = Address.class.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    //将指这是到当前对象的字段上
                    field.set(address,columValue);
                }
                return address;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

    @Test
    public void  testQuery()  {
        //1-需要将列明设置别名,别名要和javabean类的字段一致
        String sql = "select id,name,user_id userId,province from address where id = ?";
        Address address = queryForAddress(sql,1);
        System.out.println(address);
    }

查询操作流程

image.png

5.4-针对不同表的通用查询操作,返回表中一条记录

@Test
    public void  testQuery1()  {
        //1-需要将列明设置别名,别名要和javabean类的字段一致
        String sql = "select id,name,user_id userId,province from address where id = ?";
        Address address = getInstance(Address.class,sql,1);
        System.out.println(address);
        String sql2 = "select id,username,password from t_user where id = ?";
        User user = getInstance(User.class,sql2,1);
        System.out.println(user);
    }

    /**
     * 使用泛型方法做通用方法
     */
    public <T>T getInstance(Class<T> clazz, String sql, Object...args){

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData md = ps.getMetaData();
            //通过ResultSetMetaData获取结果集中的列数
            int columnCount = md.getColumnCount();
            if (rs.next()){
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++){

                    //获取每个列的列值:通过ResultSet
                    Object columValue = rs.getObject(i + 1);

                    //获取每个列的列名:通过ResultSetMetaData
                    //获取列的列名:getColumnName()
                    //String columnName = md.getColumnName(i + 1);
                    //2-获取列的别名:getColumnLabel()
                    String columnName = md.getColumnLabel(i + 1);
                    //通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    //将指这是到当前对象的字段上
                    field.set(t,columValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

5.4-针对不同表的通用查询操作,返回表中多条记录

image.png

@Test
    public void  testQuery2()  {
        //1-需要将列明设置别名,别名要和javabean类的字段一致
        String sql = "select id,username,password from t_user where id < ?";
        List<User> list = getForList(User.class,sql,9);
        list.forEach(System.out::println);
    }

    public <T> List<T> getForList(Class<T> clazz, String sql, Object...args){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < args.length; i++){
                ps.setObject(i + 1,args[i]);
            }
            rs = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData md = ps.getMetaData();
            //通过ResultSetMetaData获取结果集中的列数
            int columnCount = md.getColumnCount();
            List list = new ArrayList<T>();
            while (rs.next()){
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++){

                    //获取每个列的列值:通过ResultSet
                    Object columValue = rs.getObject(i + 1);

                    //获取每个列的列名:通过ResultSetMetaData
                    //获取列的列名:getColumnName()
                    //String columnName = md.getColumnName(i + 1);
                    //2-获取列的别名:getColumnLabel()
                    String columnName = md.getColumnLabel(i + 1);
                    //通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    //将指这是到当前对象的字段上
                    field.set(t,columValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(conn,ps,rs);
        }
        return null;
    }

5.5-使用PreparedStatement替换Statement,解决SQL注入问题

因为PrepareStatement是有预编译功能,在预编译的时候已经确定了sql语句的查询条件是and还是or,而statement是将整个sql放入数据库进行查询,通过注入
1’ or 和 =1 or ‘1’ =’1 能修改查询条件关系,让and条件变成or条件

除了解决Statement的拼串、sql问题之外,PreparedStatement还有哪些好处呢?
1.PreparedStatement操作Blob的数据,而Statement做不到。
2.Preparedstatement可以实现更高效的批量操作。同样的插入操作,用Statement插入多少数据就要校验多少次,而Preparedstatement因为预编译,语句格式是确定了的,同一个表无论插入多少数据都只要校验一次就可以了。
image.png

5.6-小结

  • 两种思想
    • 面向接口编程的思想
      • 例如:Connection conn = DriverManager.getConnection(url, username, password);,虽然Connection 是接口,但DriverManager.getConnection(url, username, password)返回的是mysql Connection具体实现类的对象。conn.prepareStatement(_sql),点进prepareStatement会发现它也是接口,其实这个时候conn连接是指向mysql Connection具体实现类,所以这里的conn.prepareStatement(_sql)其实是调用实现类重写的方法。
    • ORM思想(object relational mapping)
      • 一个数据表对应一个java类
      • 表中的一条记录对应java类的一个对象
      • 表中的一个字段对应java类的一个属性

sql是需要结合列名和表的属性名来写。注意起别名。

  • 两种技术

    • JDBC结果集的元数据:ResultSetMetaData
      • 获取列数:getColumnCount()
      • 获取列的别名:getColumnLabel()
    • 通过反射,创建指定类的对象,获取指定的属性并赋值

      四、操作BLOB类型字段

      1 MySQL BLOB类型

  • MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。

  • 插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。
  • MySQL的四种BLOB类型(除了在存储的最大信息量上不同外,他们是等同的)
    • image.png
  • 实际使用中根据需要存入的数据大小定义不同的BLOB类型。
  • 需要注意的是:如果存储的文件过大,数据库的性能会下降。
  • 如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。

    2 向数据表中插入大数据类型

    ```java //向数据表customers中插入Blob类型的字段 @Test public void testInsert() throws Exception {
    Connection conn = JDBCUtils.getConnection();
    String sql = "insert into t_user(id,username,password,email,photo)values(?,?,?,?,?)";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setObject(1,4);
    ps.setObject(2,"皇埔乡");
    ps.setObject(3,"321123");
    ps.setObject(4,"123@qwe.com");
    FileInputStream is  = new FileInputStream(new File("timg.jpg"));
    ps.setBlob(5,is);
    ps.execute();
    JDBCUtils.closeResource(conn,ps);
    is.close();
}
<a name="sZQU0"></a>
## 3 从数据表中读取大数据类型
```java
    //查询数据表customers中Blob类型的字段
    @Test
    public void testQuery3() {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            conn = JDBCUtils.getConnection();
            String sql = "select id,username,password,email,photo from t_user where id = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1,4);
            rs = ps.executeQuery();
            if (rs.next()){

                int id = rs.getInt(1);
                String name = rs.getString(2);
                String password = rs.getString(3);
                User user = new User();
                user.setId(id);
                user.setUsername(name);
                user.setPassword(password);
                System.out.println(user.toString());
                //将Blob类型的字段下载下来,以文件的方式保存在本地
                Blob photo = rs.getBlob("photo");
                is = photo.getBinaryStream();
                fos = new FileOutputStream("timg2.jpg");
                byte[] buffer =  new byte[1024];
                int len;
                while ((len = is.read(buffer)) != -1){
                    fos.write(buffer,0,len);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        JDBCUtils.closeResource(conn,ps,rs);
    }

五、批量数据操作