0
点赞
收藏
分享

微信扫一扫

JDBC笔记(二)

Python百事通 2022-04-17 阅读 36
javaidea

03、使用PreparedStatement实现CRUD操作

3.1、操作和访问数据库

  • 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。

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

    • Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。

    • PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。

    • CallableStatement:用于执行 SQL 存储过程。
      在这里插入图片描述

3.2、构建数据库连接与关闭的工具类——JDBCUtil

由于获取数据库连接与关闭数据库连接都是会反复使用的,因此可以将构建连接与关闭连接封装进工具类的成员方法,工具类如下:

工具类的构建是基于如下考虑:每次执行数据库操作时都需要获取数据库连接,执行完操作后需要关闭数据库连接,这些操作如果和其他数据库操作写在一起就会很冗余,因此考虑将其写成一个工具类的静态方法,供需要时调用。工具类中应当包含以下两部分。

3.2.1、获取数据库连接

    /**
     * 获取数据库连接
     * @return 数据库的连接
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        //1、加载配置文件
        InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
        Properties properties = new Properties();
        properties.load(is);

        //2、读取配置文件
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driverClass = properties.getProperty("driverClass");

        //3、加载驱动
        Class.forName(driverClass);

        //获取连接
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }

3.2.2、关闭数据库连接(关闭资源)

(1)关闭没有结果集的数据库连接

    /**
     * 关闭没有结果集的数据库连接
     * @param connection
     * @param sm
     */
   public static void closeResource(Connection connection, Statement sm) {
        if (sm != null) {
            try {
                sm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }     
    }

    /**
     * 关闭没有结果集的数据库连接
     * @param connection
     * @param psm
     */
    public static void closeResource(Connection connection, PreparedStatement psm) {
        if (psm != null) {
            try {
                psm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

(2)关闭有结果集的数据库连接

  • 释放ResultSetPreparedStatementConnection
  • 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放
  • 可以在finally中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
    /**
     * 关闭有结果集的数据库连接
     * @param connection
     * @param sm
     * @param rs
     */
    public static void closeResource(Connection connection, Statement sm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (sm != null) {
            try {
                sm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 关闭有结果集的数据库连接
     * @param connection
     * @param psm
     * @param rs
     */
    public static void closeResource(Connection connection, PreparedStatement psm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (psm != null) {
            try {
                psm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

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

  • 通过调用 Connection 对象的createStatement()方法创建该对象。该对象用于执行静态的SQL 语句,并且返回执行结果。

  • Statement 接口中定义了下列方法用于执行 SQL语句:

    int excuteUpdate(String sql);//执行更新操作INSERT、UPDATE、DELETE
    ResultSet executeQuery(String sql);//执行查询操作SELECT
    
  • 但是使用Statement操作数据表存在弊端:

    • 问题一:存在拼串操作,繁琐
    • 问题二:存在SQL注入问题
  • SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL 语句段或命令。如

  • 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。

  • 代码演示:

    User.java

    public class User {
        String userName;
        String password;
    
        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{" +
                    "userName='" + userName + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    

    StatementTest.java

    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.sql.Statement;
    import java.util.Scanner;
    
    public class StatementTest {
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
    
            System.out.println("用户名:");
            String userName = sc.nextLine();//以换行表示输入的结束
            System.out.println("密  码:");
            String password = sc.nextLine();
    
            String sql = "select userName,password from user where userNAme = '" + userName +"' and password = '" + password + "'";
    
            User user = get(sql, User.class);
            if (user != null) {
                System.out.println("登录成功!");
            }else {
                System.out.println("用户名或密码错误!");
            }
        }
    
        public static <T> T get(String sql, Class<T> clazz) {
            T t = null;
            Connection con = null;
            Statement sm = null;
            ResultSet rs = null;
    
            try {
                //使用工具类获取连接
                con = JDBCUtil.getConnection();
    
                sm = con.createStatement();
                rs = sm.executeQuery(sql);
    
                //获取结果集的元数据
                ResultSetMetaData rsmd = rs.getMetaData();
    
                //获取结果集的列数
                int columnCount = rsmd.getColumnCount();
    
                if (rs.next()) {
                    t = clazz.newInstance();
    
                    for (int i = 0; i < columnCount; i++) {
                        //1、获取列的名称
                        //String columnName = rsmd.getColumnName(i + 1);
                        //1、获取列的别名
                        String columnName = rsmd.getColumnLabel(i + 1);
    
                        //2、根据列名获取对应数据表中的数据
                        Object columnVal = rs.getObject(columnName);
    
                        //3、将数据表中得到的数据,封装进对象
                        Field field = clazz.getDeclaredField(columnName);
                        field.setAccessible(true);
                        field.set(t, columnVal);
                    }
                    return t;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.closeResource(con, sm, rs);
            }
            return null;
        }
    }
    

综上:

在这里插入图片描述

3.4、PreparedStatement的使用

3.4.1、PreparedStatement介绍

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

3.4.2、PreparedStatement vs Statement

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

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

Java类型 SQL类型
boolean BIT
byte TINYINT
short SAMLLINT
int INTEGER
long BIGINT
String CHAR,VARCHAR,LONGVARCHAR
byte array BINARY,VAR BINARY
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP

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

3.4.4.1、实现表数据的增删改操作

1、添加操作:(向customers表中插入数据)

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Date;
import java.text.SimpleDateFormat;

public class PreparedStatementTest {
    @Test
    public void testInsert() throws  Exception{
        Connection connection = null;
        PreparedStatement psm = null;
        //1、获取连接
        connection = JDBCUtil.getConnection();

        //2、预编译SQL语句,返回PreparedStatement实例
        String sql = "insert into customers(name,email,birth) values (?,?,?)";
        psm = connection.prepareStatement(sql);

        //3、填充占位符
        psm.setString(1,"wjk");
        psm.setString(2, "wjk123@gemail.com");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        java.util.Date date = sdf.parse("1999-09-21");
        psm.setDate(3, new Date(date.getTime()));

        //4、执行操作
        psm.execute();

        //5、关闭资源
        JDBCUtil.closeResource(connection, psm);
    }
}

2、修改操作:

    @Test
    public void testUpdate() {
        Connection connection = null;
        PreparedStatement psm = null;
        try{
            //1、获取数据库的连接
            connection = JDBCUtil.getConnection();

            //2、预编译SQL语句,返回PreparedStatement实例
            String sql = "update customers set name = ? where id = ?";
            psm = connection.prepareStatement(sql);

            //3、填充占位符
            psm.setObject(1, "dc");
            psm.setObject(2,1);

            //4、执行
            psm.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5、关闭资源
            JDBCUtil.closeResource(connection, psm);
        }
    }
  • 增删改的方式一:ps.execute()
    • 如果执行的是查询操作,有返回结果,则此方法返回true;
    • 如果是增删改操作,没有返回结果,则此时返回false
  • 增删改的方式二:ps.executeUpdata()
    • 返回一个数,该数值为修改所涉及的条目的个数。例如修改了4条数据,就返回4。

因此可以利用下面的代码段进行添加的信息回执:

int insertCount = update(sql,arg0,arg1...){
  if(insertCount>0){
    System.out.println("添加成功!");
  }
}

3.4.4.2、实现通用的增删改操作

    //通用的增删改操作,使用可变形参
    //sql语句中的占位符的个数与可变形参的长度一致
    public void update(String sql, Object ... args) {
        Connection connection = null;
        PreparedStatement psm = null;

        try {
            //1、获取数据库的连接
            connection = JDBCUtil.getConnection();

            //2、预编译SQL语句,返回PreparedStatement实例
            psm = connection.prepareStatement(sql);

            //3、填充占位符
            for (int i = 0; i < args.length; i++) {
                //小心参数的声明错误
                psm.setObject(i + 1, args[i]);
            }

            //4、执行
            psm.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5、资源的关闭
            JDBCUtil.closeResource(connection, psm);
        }
    }

    @Test
    public void testCommonUpdate() throws Exception{
        //删除操作
        //String sql = "delete from customers where id = ?";
        //update(sql , 1);

        //插入操作
//        String sql = "insert into customers(name,email,birth) values (?,?,?)";
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//        java.util.Date date = sdf.parse("1991-10-05");
//        update(sql, "xz", "xz@123.com",new Date(date.getTime()));

        //修改操作
        String sql = "update customers set email = ? where name = ?";
        update(sql, "xz222@gemail.com","xz");
    }

3.4.5、使用PreparedStatement实现查询操作

3.4.5.1、实现数据表的查询操作

Customer.java

import java.sql.Date;

/**
 * ORM编程思想:Object Relational Mapping
 * 一个数据表对应一个Java类
 * 表中的一条记录对应Java类的一个对象
 * 表中的一个字段对应Java类的一个属性
 */
public class Customer {
    private int id;
    private String name;
    private String email;
    private Date birth;

    public Customer(int id, String name, String email, Date birth) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.birth = birth;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", birth=" + birth +
                '}';
    }
}

CustomerForQuery.java

import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/***
 * @Description 针对customers表的查询操作
 */
public class CustomerForQuery {

    @Test
    public void testQuery1(){

        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            connection = JDBCUtil.getConnection();
            String sql = "select id, name, email, birth from customers where id = ?";
            ps = connection.prepareStatement(sql);
            ps.setObject(1, 2);

            //执行,返回结果集
            rs = ps.executeQuery();

            //处理结果集
            if (rs.next()) {//next():判断结果集的下一条是否有数据,如果有数据返回true,并指针下移;如果返回false,则指针不下移
                //获取当前这条数据的各个字段值
                int id  = rs.getInt(1);
                String name = rs.getString(2);
                String email = rs.getString(3);
                Date birth = rs.getDate(4);

                //方式一:
                System.out.println("id = " + id + ", name = " + name + ", email = "+ email + ", birth = " + birth);

                //方式二:
                Object[] data = new Object[]{id, name, email, birth};

                //方式三:将数据封装成一个对象(推荐)
                Customer customer = new Customer(id,name,email,birth);
                System.out.println(customer);

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            JDBCUtil.closeResource(connection, ps, rs);
        }
    }
}

3.4.5.2、实现通用的查询操作

1、针对于customers表的通用查询操作

    /**
     *针对于customers表的通用查询操作
     */
    public Customer queryForCustomers(String sql, Object ... args) {
        Connection connection = null;
        PreparedStatement psm = null;
        ResultSet rs = null;
        try {
            connection = JDBCUtil.getConnection();

            psm = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                psm.setObject(i + 1, args[i]);
            }

            rs = psm.executeQuery();

            //获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //通过ResultSetMetaData获取结果集中的列表
            int columnCount = rsmd.getColumnCount();

            if (rs.next()) {
                Customer customer = new Customer();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);

                    //获取每个列的列名
                    String columnName = rsmd.getColumnName(i + 1);

                    //给customer对象指定的columnName属性赋值columnValue,通过反射
                    Field field = Customer.class.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(customer, columnValue);
                }
                return customer;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.closeResource(connection, psm, rs);
        }
        return null;
    }

    @Test
    public void testQueryForCustomer() {
        String sql = "select id ,name, email, birth from customers where id = ?";
        Customer customer = queryForCustomers(sql, 3);
        System.out.println(customer);
    }

2、针对于Order表的通用查询操作(与Customer表不同的是,order表中列名与Order类中的属性名不同

import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.*;

/**
 * 针对于Order表的通用查询操作
 */
public class OrderForQuery { 
    /**
     * 针对于表打的字段名与类的属性名不相同的情况:
     * 1、必须声明sql时,使用类的属性名来命名字段的别名
     * 2、使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(),获取列的别名
     * 
     * 说明:如果sql中没有给字段取别名,getColumnLabel()获取的就是列名
     */
    @Test
    public void testQuery() {

        String sql = "select order_id orderId, order_name orderName, order_date orderDate from `order` where order_id = ?";
        Order order = orderForQuery(sql,1);
        System.out.println(order);
    }

    /**
     * 针对于Order表的通用查询操作
     * @return
     */

    public Order orderForQuery(String sql, Object... args) {
        Connection connection = null;
        PreparedStatement psm = null;
        ResultSet rs = null;
        try {
            connection = JDBCUtil.getConnection();
            psm = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                psm.setObject(i + 1, args[i]);
            }
            //执行,获取结果集
            rs = psm.executeQuery();

            //获取结果集的元数据
            ResultSetMetaData rsmd= rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();

            if (rs.next()) {
                Order order = new Order();
                for(int i = 0; i < columnCount; i++) {
                    //获取每个列的列值;通过ResultSet
                    Object columValue = rs.getObject(i + 1);
                    //getColumnName():获取每个列的列名;通过ResultMetaData-------不推荐使用
                    //String columnName = rsmd.getColumnName(i + 1);

                    //getColumnLabel():获取每个列的别名;通过ResultMetaData
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    //通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
                    Field field = Order.class.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(order, columValue);

                }
                return order;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.closeResource(connection,psm,rs);
        }
        return null;
    }
}

3.4.5.3、查询操作的流程

在这里插入图片描述

3.4.5.4、实现不同表的通用的查询操作

1、返回表中查询的一条记录

package jdbcTest;

import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用PreparedStatement实现不同表的通用的查询操作
 */
public class PreparedStatementQueryTest {
    @Test
    public void testGetInstance() {
        String sql = "select id, name, email from customers where id = ? ";
        Customer customer = getInstance(Customer.class, sql, 2);
        System.out.println(customer);

        String sql1 = "select order_id orderId, order_name orderName from `order` where order_id = ?";
        Order order = getInstance(Order.class, sql1, 1);
        System.out.println(order);
    }

    /**
     * 针对于不同的表的通用查询操作,返回表中的一条记录
     * @param clazz
     * @param sql
     * @param args
     * @param <T>
     * @return
     */
    public <T> T getInstance(Class<T> clazz,String sql, Object ... args) {
        Connection connection = null;
        PreparedStatement psm = null;
        ResultSet rs = null;
        try {
            connection = JDBCUtil.getConnection();

            psm = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                psm.setObject(i + 1, args[i]);
            }

            rs = psm.executeQuery();

            //获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //通过ResultSetMetaData获取结果集中的列表
            int columnCount = rsmd.getColumnCount();

            if (rs.next()) {
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);

                    //获取每个列的别名
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    //给t对象指定的columnName属性赋值columnValue,通过反射
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t, columnValue);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.closeResource(connection, psm, rs);
        }
        return null;
    }
}

2、返回表中的多条记录

package jdbcTest;

import org.junit.Test;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * 使用PreparedStatement实现不同表的通用的查询操作
 */
public class PreparedStatementQueryTest {
    @Test
    public void testGetForList() {
        String sql = "select id, name, email from customers ";
        List<Customer> list = getForList(Customer.class, sql);
        list.forEach(System.out :: println);

        String sql1 = "select order_id orderId, order_name orderName from `order` where order_id < ?";
        List<Order> list1 = getForList(Order.class, sql1, 3);
        list1.forEach(System.out :: println);

    }

    /**
     * 针对于不同的表的通用查询操作,返回表中的多条记录
     * @param clazz
     * @param sql
     * @param args
     * @param <T>
     * @return
     */
    public <T> List<T> getForList(Class<T> clazz, String sql, Object ... args) {
        Connection connection = null;
        PreparedStatement psm = null;
        ResultSet rs = null;
        try {
            connection = JDBCUtil.getConnection();

            psm = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                psm.setObject(i + 1, args[i]);
            }

            rs = psm.executeQuery();

            //获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
            ResultSetMetaData rsmd = rs.getMetaData();
            //通过ResultSetMetaData获取结果集中的列表
            int columnCount = rsmd.getColumnCount();

            //创建集合对象
            ArrayList<T> list = new ArrayList<>();

            while (rs.next()) {
                T t = clazz.newInstance();
                //处理结果集一行数据中的每一个列;给t对象指定的属性赋值
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);

                    //获取每个列的别名
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    //给t对象指定的columnName属性赋值columnValue,通过反射
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t, columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.closeResource(connection, psm, rs);
        }
        return null;
    }
}

3.5、ResultSet与ResultMetaData

3.5.1、ResultSet

  • 查询需要调用PreparedStatementexecuteQuery()方法,查询结果是一个ResultSet 对象
  • ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet接口由数据库厂商提供实现。
  • ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
  • ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象 的 next()方法移动到下一行。调用 next()方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。 相当于Iterator对象的hasNext()next() 方法的结合体。
  • 当指针指向一行时, 可以通过调用 getXxx(int index)getXxx(int columnName) 获取每一列的值。
    • 例如: getInt(1),getString("name")
    • 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始
  • ResultSet接口的常用方法:
    • boolean next()
    • getString()
      在这里插入图片描述

3.5.2、ResultSetMetaData

  • 可用于获取关于 ResultSet对象中列的类型和属性信息的对象。

  • ResultSetMetaData meta = rs.getMetaData();

    • getColumnName(int column):获取指定列的名称
    • getColumnLabel(int column):获取指定列的别名
    • getColumnCount():返回当前 ResultSet 对象中的列数。
    • getColumnTypeName(int column):检索指定列的数据库特定的类型名称。
    • getColumnDisplaySize(int column):指示指定列的最大标准宽度,以字符为单位。
    • isNullable(int column):指示指定列中的值是否可以为 null。
    • isAutoIncrement(int column):指示是否自动为指定列进行编号,这样这些列仍然是只读的。
      在这里插入图片描述

问题1:得到结果集后, 如何知道该结果集中有哪些列 ? 列名是什么?

需要使用一个描述 ResultSet 的对象, 即 ResultSetMetaData

问题2:关于ResultSetMetaData

1、如何获取ResultSetMetaData:调用 ResultSetgetMetaData()方法即可。

2、获取 ResultSet 中有多少列:调用 ResultSetMetaDatagetColumnCount() 方法。

3、获取 ResultSet 每一列的列的别名是什么:调用 ResultSetMetaDatagetColumnLabel() 方法。

3.6、JDBC API小结

  • 两种思想

    • 面向接口编程的思想
    • ORM思想(object relational mapping)
      • 一个数据表对应一个java类
      • 表中的一条记录对应java类的一个对象
      • 表中的一个字段对应java类的一个属性
  • 两种技术

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

3.7、章节练习

1、练习题1:从控制台向数据库的表customers中插入一条数据,表结构如下:
在这里插入图片描述

import jdbcTest.JDBCUtil;
import org.junit.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Scanner;

public class exercise1 {
    //通用的增删改操作,使用可变形参
    //sql语句中的占位符的个数与可变形参的长度一致
    public int update(String sql, Object... args) {
        Connection connection = null;
        PreparedStatement psm = null;

        try {
            //1、获取数据库的连接
            connection = JDBCUtil.getConnection();

            //2、预编译SQL语句,返回PreparedStatement实例
            psm = connection.prepareStatement(sql);

            //3、填充占位符
            for (int i = 0; i < args.length; i++) {
                //小心参数的声明错误
                psm.setObject(i + 1, args[i]);
            }

            //4、执行
            /**
             * psm.execute();
             * 如果执行的是查询操作,有返回结果,则此方法返回true
             * 如果执行的是增删改操作,没有返回结果,则此方法返回false
             */
            //psm.execute();

            return psm.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //5、资源的关闭
            JDBCUtil.closeResource(connection, psm);
        }
        return 0;
    }

    @Test
    public void test() {
        Scanner sc = new Scanner(System.in);

        //从控制台输入要插入的数据
        System.out.println("请输入姓名:");
        String name = sc.nextLine();
        System.out.println("请输入生日(年-月-日):");
        String birth = sc.nextLine();
        System.out.println("请输入邮箱:");
        String email = sc.nextLine();

        String sql = "insert into customers(name, birth, email) values (?,?,?)";

        int insertCount = update(sql,name,birth,email);
        if (insertCount > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");
        }
    }
}

2、创立数据库表 examstudent,表结构如下:
在这里插入图片描述
向数据表中添加如下数据:
在这里插入图片描述

代码实现1:插入一个新的student 信息

请输入考生的详细信息

Type:

IDCard:

ExamCard:

StudentName:

Location:

Grade:

信息录入成功!

    @Test
    public void test() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入考试类型:");
        int type = sc.nextInt();
        System.out.println("请输入考生身份证号:");
        String IDCard = sc.next();
        System.out.println("请输入考生准考证号:");
        String examCard = sc.next();
        System.out.println("请输入学生姓名:");
        String StudentName = sc.next();
        System.out.println("请输入区域:");
        String Location = sc.next();
        System.out.println("请输入成绩:");
        int grade = sc.nextInt();

        String sql = "insert into student(Type, IDCard, ExamCard, StudentName, Location, Grade) values(?,?,?,?,?,?)";
        int insertCount = update(sql, type, IDCard, examCard, StudentName, Location, grade);
        if (insertCount > 0) {
            System.out.println("信息录入成功!");
        } else {
            System.out.println("信息录入失败!");
        }
    }

代码实现2:在 eclipse中建立 java 程序:输入身份证号或准考证号可以查询到学生的基本信息。结果如下:
在这里插入图片描述

Student.java

package jdbcExercise;

import org.junit.Test;

public class Student {
    private int FlowID;
    private int Type;//四级/六级
    private String IDCard;//身份证号
    private String ExamCard;//准考证号
    private String StudentName;
    private String Location;//区域
    private int Grade;

    public Student() {
        super();
    }

    public Student(int flowId, int type, String IDCard, String examCard, String studentName, String location, int grade) {
        FlowID = flowId;
        Type = type;
        this.IDCard = IDCard;
        ExamCard = examCard;
        StudentName = studentName;
        Location = location;
        this.Grade = grade;
    }

    public int getFlowID() {
        return FlowID;
    }

    public void setFlowId(int flowId) {
        FlowID = flowId;
    }

    public int getType() {
        return Type;
    }

    public void setType(int type) {
        Type = type;
    }

    public String getIDCard() {
        return IDCard;
    }

    public void setIDCard(String IDCard) {
        this.IDCard = IDCard;
    }

    public String getExamCard() {
        return ExamCard;
    }

    public void setExamCard(String examCard) {
        ExamCard = examCard;
    }

    public String getStudentName() {
        return StudentName;
    }

    public void setStudentName(String studentName) {
        StudentName = studentName;
    }

    public String getLocation() {
        return Location;
    }

    public void setLocation(String location) {
        Location = location;
    }

    public int getGrade() {
        return Grade;
    }

    public void setGrade(int grade) {
        this.Grade = grade;
    }

    @Override
    public String toString() {
        System.out.println("========查询结果========");
        return info();
    }

    private String info() {
        return "流水号:" + FlowID + "\n四级/六级:" + Type +
                "\n身份证号:" + IDCard + "\n准考证号:" + ExamCard +
                "\n学生姓名:" + StudentName + "\n区域:" + Location + "\n成绩:" + Grade;
    }
}

Exercise2Test.java

@Test
    public void testQuery() {
        Scanner sc = new Scanner(System.in);
        boolean flag = true;
        while (flag) {
            System.out.println("请输入您要输入的类型:\n a:准考证号\n b:身份证号\n");
            String type = sc.next();
            if (!type.equalsIgnoreCase("a") && !type.equalsIgnoreCase("b")){
                System.out.println("您的输入错误,请重新进入程序!");
                continue;
            }
            char c = type.charAt(0);
            String sql = "";
            switch (c) {
                case 'a':
                    sql = "select * from student where ExamCard = ?";
                    System.out.println("请输入准考证号:");
                    break;
                case 'b':
                    sql = "select * from student where IDCard = ?";
                    System.out.println("请输入身份证号:");
                    break;
            }
            String card = sc.next();
            Student student = getStudent(sql, card);
            if (student == null) {
                System.out.println("查无此人!请重新进入程序!");
            } else {
                System.out.println(student);
                flag = false;
            }
        }
    }

    /**
     * 获取查询学生信息
     * @param sql
     * @param args
     * @return
     */ 
     public Student getStudent(String sql, Object ... args) {
        Connection connection = null;
        PreparedStatement psm = null;
        ResultSet rs = null;

        try {
            connection = JDBCUtil.getConnection();
            psm = connection.prepareStatement(sql);

            for (int i = 0; i < args.length; i++) {
                psm.setObject(i + 1, args[i]);
            }

            //查询,返回结果集
            rs = psm.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取列数
            int columnCount = rsmd.getColumnCount();
            if (rs.next()) {
                Student student = new Student();

                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);

                    Field field = Student.class.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(student, columnValue);
                }
                return student;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.closeResource(connection, psm, rs);
        }
        retu

代码实现3:完成学生信息的删除功能
在这里插入图片描述

    @Test
    public void testDelete() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学生的考号:");
        String examCard = sc.next();

        String sql = "delete from student where ExamCard = ?";
        int deleteCount = update(sql, examCard);
        if (deleteCount > 0) {
            System.out.println("删除成功!!!");
        } else {
            System.out.println("查无此人,请重新输入!!!");
        }

    }
举报

相关推荐

0 条评论