Java高级-JDBC

一.JDBC简介

JDBC(Java Data Base Connectivity)是Java程序员操作数据库的API,也是Java程序与数据库交互的一门技术。JDBC是Java操作数据库的规范,由一组用Java语言编写的类与接口组成,它对数据库的操作提供了基本方法,但对于数据库的细节操作由数据库厂商进行实现。JDBC对于Java程序员而言,是一套标准的操作数据库的API;而对数据库厂商而言,又是一套标准的模型接口。

                                       

JDBC最终是为了实现以下目标:

  • 通过使用标准的SQL语句,甚至是专门的SQL扩展,程序员就可以利用Java语言开发访问数据库的应用,同时还依旧遵守Java语言的相关约定。
  • 数据库供应商和数据库工具开发商可以提供底层的驱动程序。因此,他们可以优化各自数据库产品的驱动程序。

JDBC规范将驱动程序归结为以下几类:

  • 第1类驱动程序将JDBC翻译成ODBC(ODBC为C语言访问数据库提供了一套编程接口),然后使用一个ODBC驱动程序与数据库进行通信。
  • 第2类驱动程序是由部分Java程序和部分本地代码组成的,用于与数据库的客户端的API进行通信。
  • 第3类驱动程序是纯Java客户端类库,它使用一种与具体数据库无关的协议将数据库请求发送给服务器构件,然后该构件再将数据库请求翻译成数据库相关的协议。

二.JDBC连接数据库的过程

1.注册数据库驱动

连接数据库前,需要将数据库厂商提供的数据库驱动类注册到JDBC的驱动管理器,通常情况下,是通过将数据库驱动加载JVM来实现的。

//Class的forName方法作用是将指定字符串名的类加载到JVM中。在加载后,数据库驱动程序会把驱动类自动注册到驱动管理器
Class.forName("com.mysql.jdbc.Driver");

2.构建数据库连接URL

要建立数据库连接,就要构建数据库连接的URL,这个URL由数据库厂商指定,不同的数据库,它的URL也有所区别,但都符合一个基本的格式,即“JDBC协议+IP地址或域名+端口+数据库名称”。如MySQL的数据库连接URL的字符串为“jdbc:mysql://localhost"3306/test”。

3.获取Connection对象

在注册了数据库驱动及构建数据库URL后,就可以通过驱动管理器获取数据库连接Connection。Connection对象是JDBC封装的数据库连接对象,只有创建此对象后,才可以对数据进行相关操作。获取方法如下:

//Connection对象的获取需要用到DriverManager对象,DriverManager的getConnection方法通过数据库连接URL,数据库用户名和数据库密码创建Connection对象
DriverManager.getConnection(url,username,password);

注意:在JDK中,不包含数据库的驱动程序,使用JDBC操作数据库,需要事先下载数据库厂商提供的驱动包。例如使用MySQL数据库,就要添加MySQL官方提供的数据库驱动包。

三.JDBC API

JDBC是Java操作数据库的规范,由一组用Java语言编写的类与接口组成,也就是JDBC API。

1.Connection接口

Connection接口位于java.sql包中,是与特定数据库的连接会话,只有获得特定数据库的连接对象,才能访问数据库,操作数据库中的数据表,视图和存储过程等。Connection接口的常用方法声明及说明如下:

方法声明说明
void close() throws SQLException立即释放Connection对象的数据库连接占用的JDBC资源,在操作数据库后,立即调用该方法
void commit() throws SQLException提交事务,并释放Connection对象当前持有的所有数据库锁。当事务被设置为手动提交模式时,需要使用该方法提交事务
Statement createStatement() throws SQLException创建一个Statement对象来讲SQL语句发送到数据库,该方法返回Statement对象
PreparedStatement preparedStatement(String sql) throws SQLException将参数化的SQL语句预编译并存储在PreparedStatement对象中,并返回创建的对象。
DatabaseMetaData getMetaData() throws SQLException获取Connection对象所连接的数据库的元数据DataBaseMetaData对象,元数据包括关于数据库的表,受支持的SQL语法,存储过程,此连接功能等信息。
boolean isClosed() throws SQLException判断Connection对象是否与数据库断开连接,该方法返回布尔值。需要注意的是,如果断开连接,则不能再使用Connection对象操作数据库。
void rollback() throws SQLException回滚事务,并释放Connection对象持有的所有数据库锁,注意该方法需要应用于Connection对象的手动提交模式。

2.DriverManager类

使用JDBC操作数据库,需要使用数据库厂商提供的驱动程序,通过驱动程序可以与数据库进行交互。DriverManager类主要作用于用户及驱动程序之间,它是JDBC中的管理层,通过DriverManager类可以管理数据库厂商提供的驱动程序,并建立应用程序与数据库之间的连接,其方法声明及说明如下:

方法声明说明
public static void deregisterDriver(Driver driver) throws SQLException从DriverManager的管理列表中删除一个驱动程序,参数driver为要删除的驱动对象。
public static Connection getConnection(String url) throws SQLException根据指定数据库连接URL,建立与数据库连接Connection。参数url为数据库连接URL
public static Connection getConnection(String url,Properties info) throws SQLException根据指定数据库连接URL及数据连接属性信息建立与数据库连接Connection。
public static Connection getConnection(String url,String user,String password) throws SQLException根据指定数据库连接URL,用户名和密码建立与数据库连接Connection。参数url为数据库连接URL
public static Enumeration<Driver> getDrivers()获取当前DriverManager中已经加载的驱动程序。
public static void registerDriver(Driver driver) throws SQLException向DriverManager注册一个驱动对象。

3.Statement接口

在创建数据库连接之后,就可以通过程序来调用SQL语句对数据库进行操作,在JDBC中Statement接口封装这些操作。Statement接口提供了执行语句和获取结果的基本方法。

方法声明说明
void addBatch() throws SQLException将SQL语句添加到Statement对象的当前命令行列表中,该方法用于SQL命令的批处理。
void clearBatch() throws SQLException清空Statement对象中的命令列表。
void close() throws SQLException立即释放Statement对象的数据库和JDBC资源。
boolean execute(String sql) throws SQLException

执行指定的SQL语句。如果SQL语句返回结果,该方法返回true。

int[] executeBatch() throws SQLException

将一批命令提交给数据库执行,返回更新计数组成的数组。

ResultSet executeQuery(String sql) throws SQLException执行查询类型的SQL语句
Connection getConnection() throws SQLException获取生成Statement对象的Connection对象
boolean isClosed() throws SQLException判断Statement对象是否被关闭。

4.PreparedStatement接口

PreparedStatement接口继承与Statement接口,拥有Statement接口中的方法,而且PreparedStatement接口针对带参数SQL语句的执行操作进行了扩展。应用于PreparedStatement接口中的SQL语句,可以使用占位符?来代替SQL语句中的参数,然后再对其进行赋值。常用方法如下:

方法声明说明
void setInt(int parameterIndex,int x) throws SQLException将int值x作为SQL语句中的参数值,parameterIndex作为参数位置的索引
...(float double类型和setInt类似) 

5.ResultSet接口

执行SQL语句的查询语句会返回查询的结果集,在JDBC API中,使用ResultSet对象接收查询结果集。

ResultSet接口位于java.sql包中,封装了数据查询的结果集。ResultSet对象包含了符合SQL语句的所有行,,针对Java中的数据类型提供了一套getXXX()方法,通过这些方法可以获取每一行中的数据。ResultSet还提供光标的功能,通过光标可以自由定位到某一行中的数据。常用方法如下:

方法声明说明
boolean absolute(int row) throws SQLException将光标移动到ResultSet对象的给定行编号
void afterLast() throws SQLException将光标移动到ResultSet对象的最后一行之后,如果结果集中不包含任何行,该方法无效
InputStream getBinaryStream(String columnLabel) throws SQLException以byte流的方法获取ResultSet对象当前行的指定列的值
int getInt(Strinf columnLabel)throws SQLException以int的方式获取ResultSet对象当前行中的指定列的值
boolean previous() throws SQLException将光标位置向前移动一行

四.JDBC 操作数据库

1.添加,删除,更新,查询

添加数据:通过JDBC向数据库添加数据,可以使用insert语句实现插入数据的SQL语句,对于SQL语句中的参数可以使用占位符?代替,然后通过PreparedStatement对其赋值并执行SQL(在执行预备语句之前,必须使用set方法将变量绑定到实际值上。使用PreparedStatement对象对SQL语句的占位符参数赋值,其参数的下标值不是0,而是1)。进行赋值后,需要调用executeUpdate方执行操作,更新数据库中的信息。

查询数据:查询的过程与添加的流程基本相同,但执行查询数据操作后需要通过一个对象装载查询结果集,这个对象就是ResultSet对象。ResultSet集合所查询的数据位于集合的中间位置,在第一条数据之前和最后一条数据之后都有一个位置。默认情况下,ResultSet的光标位置在第一行数据之前,所以在第一次获取数据就需要移动光标位置。

                                            

修改数据:操作方法与添加数据类似,使用update语句实现。在实际开发中,通常情况下都是由程序传递SQL语句中的参数,所以修改数据需要使用PreparedStatement对象进行操作。

删除数据:操作方法与添加数据类似,使用delete语句实现。在实际开发中,通常情况下都是由程序传递SQL语句中的参数,删除数据需要使用PreparedStatement对象进行操作。

2.读写LOB

在SQL中,二进制大对象称为BLOB,字符型大对象称为CLOB。

要读取LOB,需要执行SELECT语句,然后ResultSet上调用getBlob或getClob方法,这样就可以获得Blob或Clob类型的对象。要从Blob中获取二进制数据,可以调用getBytes或getBinaStram。要从Clob中获取其中的字符数据,可以调用getSubString或getCharacterStream。

要将LOB置于数据库中,需要在Connection对象上调用createBlob或createClob,然后获取一个用于该LOB的输出流或写出器,写出数据,并将该对象存储到数据库中。

java.sql.Blob的方法如下:

方法声明说明
long length()获取该BLOB的长度
byte[] getBytes(long startPosition,long length)获取该BLOB中的给定范围的数据

InputStream getBinaryStream()

InputStream getBinaryStream(long startPosition,long length)

返回一个输入流,用于读取该BLOB中全部或给定范围的数据
OutputStream setBinaryStream(long startPosition)返回一个输出流,用于从给定位置开始写入该BLOB

java.sql.Clob

方法声明说明
long length()获取该CLOB中的字符总数
String getSubString(long startPosition,long length)获取该CLOB中给定范围的字符

Reader getCharacterStream()

Reader getCharacterStream(long startPosition,long length)

返回一个读入器,用于读取该CLOB中全部或给定范围的数据
Writer setCharacterStream(long startPosition)

返回一个写出器,用于从给定位置开始写入该CLOB

3.多结果集

在执行存储过程,或者在使用允许单个查询中提交多个select语句的数据库时,一个查询有可能返回多个结果集。下面是获取所有结果集的步骤:

(1)使用execute方法执行SQL语句。

(2)获取第一个结果集或更新计数。

(3)重复调用getMoreResults方法以移动下一个结果集。

(4)当不存在更多的结果集或更新计数时,完成操作。

如果由多结果集的链中的下一项是结果集,execute和getMoreResults方法将返回true,而如果在链中的下一项不是更新计数,getUpdateCount方法将返回-1。

4.获取自动生成的键

JDBC没有提供独立于提供商的自动生成建的解决方法,但是它提供了获取自动生成建的有效途径。当向数据表中插入一个新行,且其键自动生成时,可以用下面的代码获取这个键:

stat.executeUpadate(insertStatement,Statement.RETURN_GENERATED_KEYS);
ResultSet rs=stat.getGeneratedKeys();
if(rs.next)
{
    int key=rs.getInt(1);
   ...
}

5.可滚动和可更新的结果集

可滚动的结果集

默认情况下,结果集是不可滚动进而不可更新的。为了从查询中获取可滚动的结果集,必须使用下面的方法得到一个不同的Statement对象:

Statement stat=conn.createStatement(type,concurrency);

如果要获得预备语句,请调用下面的方法:

PreparedStatement stat=conn.preparedStatement(command,type,concurrency);

type和concurrency参数都是有关于结果集的取值。

ResultSet类的type值:

解释
TYPE_FORWARD_ONLY结果集不能滚动(默认值)
TYPE_SCROLL_INSENSITIVE结果集可以滚动,但是对数据库变化不敏感
TYPE_SCROLL_SENSITIVE结果集可以滚动,且对数据库变化敏感

ResultSet的Concurrency值:

解释
CONCUR_READ_ONLY结果集不能用于更新数据库(默认值)
CONCUR_UPDATABLE结果集可以用于更新数据库

并非所有的数据库驱动程序都支持可滚动和可更新的结果集。使用DataBaseMetaData接口中的supportResultSetType和supportResultSetConcurrency方法,可以获知在使用特定的驱动程序时,某个数据库究竟支持哪些结果集类型和哪些并发模式。即便数据库支持所有的结果集模式,某个特定的查询也可能无法产生带有所请求的所有属性的结果集(例如,一个复杂查询的结果集就有可能是不可更新的结果集)在这种情况下,executeQuery方法将返回一个功能较少的ResultSet对象,并添加一个SQLWarning到连接对象中。或者,也可以使用ResultSet接口中的getType和getConcurrency方法查看结果集实际支持的模式。如果不检查结果集的功能就发起一个不支持的操作,比如对不可滚动的结果集调用previous方法,程序会抛出SQLException异常。

可更新的结果集

如果希望编辑结果集中的数据,并且将结果集上的数据变更自动反应到数据库中,那么必须使用可更新的结果集。应该使用下面的方法创建一条语句:

Statement stat=conn.createStatement(Result.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATEABLE);

并非所有的查询都会返回可更新的结果集,如果查询涉及多个表的连接操作,那么它所产生的结果集将是不可更新的,如果查询只涉及一个表,或者在查询时是使用主键连接多个表,那么它产生的结果集将是可更新的结果集。可以调用ResultSet接口中的getConcurrency方法来确定结果集是否可更新。

要注意的是,updateXxx方法改变的是结果集中的行值,而非数据库中的值。当更新完行中的字段值后,必须调用updateRow方法,这个方法将当前行中的所有更新信息发送给数据库。还可以调用cancelRowUpdates方法来取消对当前行的更新。

6.行集

可滚动的结果集虽然功能强大,但是有一个重要的缺陷:在于用户交互的过程中,始终与数据库保持连接。RowSet接口扩展自ResultSet接口,却无需始终保持与数据库的连接。

构建行集:以下是javax.sql.rowset包提供的接口,扩展了RowSet接口。

  • CachedRowSet允许在断开的状态下执行相关操作。
  • WebRowSet对象代表了一个被缓存的行集,该行集可以被保存为XML文件。该文件可以移动到Web应用的其他层中,只要在该层使用另一个WebRowSet对象重新打开文件即可。
  • FIlteredRowSet和JoinRowSet接口支持对行集的轻量级操作,等同SQL中的SELECT和JOIN操作。这两个接口的操作对象时存储在行集中的数据,因此运行时无需建立数据库连接。
  • JdbcRowSet是ResultSet接口中的一个瘦包装器。在RowSet接口中添加了有用的方法。

被缓存的行集:包含了一个结果集中的所有数据。CachedRowSet是ResultSet接口的子接口,可以像使用结果集一样来使用行集。被缓存的行集有一个优点:断开数据库连接后仍然可以使用行集。在执行每个用户命令时,只需要打开数据库连接,执行查询操作,将查询结果放入被缓存的行集,然后关闭数据库连接即可。甚至可以修改被缓存的行集,必须发起一个显式的请求,以便让数据库真正接收所有修改。此时CachedRowSet类会重新连接到数据库,并通过执行SQL语句向数据库写入所有修改后的数据。

可以使用一个结果集来填充CachedRowSet对象。

ResultSet result=...;
RowSetFactory f=RowSetProvider.newFactory();
CachedRowSet c=f.createCachedRowSet();
crs.populate(result);
conn.close();

或者,也可以让CachedRowSet对象自动建立一个数据库连接。

ResultSet result=...;
RowSetFactory f=RowSetProvider.newFactory();
CachedRowSet c=f.createCachedRowSet();
//设置数据库参数
c.setURL("驱动");
c.setUsername("");
c.setPassword("");
//设置查询语句和所有参数
c.setCommand("查询语句");
c.setString(参数);
//将查询结果填充到行集
c.execute();

javax.sql.rowset.CachedRowSet常用的方法声明和说明如下:

方法声明说明
void execute(Connection conn)

通过执行使用setCommand方法设置的语句集来填充行集,该方法使用给定的连接,并负责关闭它。

void populate(ResultSet result)将指定的结果集中的数据填充到被缓存的行集中。
void acceptChanges(Connection conn)重新连接数据库,并写回行集中修改过的数据。如果因为数据库中的数据已经被修改而导致无法写回行集中的数据,该方法可能会抛出SyncProviderException异常。

7.事务

默认情况下,数据库连接处于自动提交模式。每个SQL语句一旦执行便被提交到数据库。一旦命令被提交,就无法对它执行回滚操作。在使用事务时,需要关闭这个默认值:

conn.setAutoCommit(false);

使用事务的步骤:

//创建一个语句对象
Statement stat=conn.createStatement();
//任意多次调用executeUpdate方法
stat.executeUpdate(command1);
stat.executeUpdate(command2);
stat.executeUpdate(command3);
...
//执行所有命令没有出错,则调用commit方法
conn.commit();
//如果出现错误
conn.rollback();

在使用某些驱动程序时,使用保存点可以更细粒度地控制回滚操作。创建一个保存点意味着稍后只需返回这个点,而非事务的开头。

Statement stat=conn.createStatement();
stat.executeUpadate(command1);
Savepoint s=conn.setSavepoint();
stat.executeUpdate(command2);
if(...) connn.rollback(svpt);
...
conn.commot();

批量更新:一个语句序列作为一批操作将同时被收集和提交。使用DataBaseMetaData接口中的supportsBatchUpdates方法可以获知数据库是否支持这种特性。

处于同一批的语句可以是insert,update和delete语句。也可以是数据库定义语句。但是在批量处理中添加SELECT语句会抛出异常(从概念上讲,批量处理中的SELECT语句没有意义,因为它会返回结果集,并不更新数据库)。

操作步骤:

//创建一个语句对象
Statement stat=conn.createStatement();
//调用addBatch方法
String command="...";
stat.addBatch(command);

while(...)
{
     command="...";
     stat.addBatch(command);
}

//提交整个批量更新语句
int[] counts=stat.executeBatch();

//这个回滚的时候,记住关闭自动提交模式

五.JDBC在Java Web中的应用

1.开发模式

在Java Web开发中使用JDBC,应遵循MVC的设计思想,从而使Web程序拥有一定的健壮性和可扩展性。JDBC处于MVC的模型层位置:

客户端通过JSP页面与程序进行交互,对于数据的增删改查请求由Servelt对其进行分发处理,如Servelt接收到添加数据请求,就会分发给增加数据的JavaBean对象,而真正的数据库操作是用过JDBC封装的JavaBean进行实现。

2.分页查询

2.1通过ResultSet的光标实现分页

ResultSet是JDBC API中封装的查询结果集对象,通过该对象可以实现数据的分页显式。在ResultSet对象中,有一个“光标”的概念,光标通过上下移动定位查询结果集中的行,从而获取数据。所以通过Result的移动光标,可以设置ResultSet对象中的记录的起始位置和结束位置,来实现数据的分页显式。

通过ResultSet的光标实现分页,优点是在各种数据库上通用,缺点是占用大量资源,不适合数据量大的情况。

2.2 通过数据库机制实现分页

很多数据库自身提供了分页机制,如MySQL数据库中提供limit关键字。其优点是减少数据库资源的消耗,提高程序的性能;缺点是只针对某一数据库通用。

例子:通过MySQL数据库提供的分页机制,实现商品信息的分页查询功能,将分页数据显式在JSP页面中。

首先创建一个名为productbase的数据库,并且在其中建立一个表tb_product表,在这个表中添加数据。如图所示:

创建一个名称为Product的类,用于封装商品信息,该类时商品信息的JavaBean。代码如下:

package test;

public class Product {
    public static final int PAGE_SIZE=2;   //每页的记录数
    private int id;                        //姓名
    private String name;                   //名字
    private double price;                  //价格
    private int num;                      //数量
    private String unit;                  //单位

    public String getName() {
        return name;
    }

    public double getPrice() {
        return price;
    }

    public int getId() {
        return id;
    }

    public int getNum() {
        return num;
    }

    public String getUnit() {
        return unit;
    }

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

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

    public void setNum(int num) {
        this.num = num;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

}

创建名称为ProductDao的类,主要封装商品信息的数据库相关操作。代码如下:

package test;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class ProductDao {
    
/*
获取数据库连接
 */
    public Connection getConnetion()
    {
        Connection conn=null;
        try
        {
            Class.forName("com.mysql.jdbc.Driver");   //加载数据库驱动,注册到驱动管理器
            String url="jdbc:mysql://localhost:3306/productbase?serverTimezone=UTC";   //数据库连接字符串
            String name="root";     //数据库用户名
            String password="password";   //数据库密码
            conn= DriverManager.getConnection(url,name,password);  //创建数据库连接
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /*
    分页查询所有商品信息
    这个方法中使用limit关键字实现分页查询
    limit arg1,agr2 
    arg1:用于指定查询记录的起始位置
    arg2:用于指定查询数据所返回的记录数
 
     */

    public List<Product> find(int page)
    {
        List<Product> list=new ArrayList<Product>();
        Connection connection=getConnetion();
        String sql="select * from tb_product order by id desc limit ?,?";  //分页查询SQL语句
        try
        {
            PreparedStatement ps=connection.prepareStatement(sql);   
            ps.setInt(1,(page-1)* Product.PAGE_SIZE);
            ps.setInt(2, Product.PAGE_SIZE);
            ResultSet resultSet=ps.executeQuery();
            while(resultSet.next())
            {
                Product p=new Product();    //赋值
                p.setId(resultSet.getInt("id"));
                p.setName(resultSet.getString("name"));
                p.setPrice(resultSet.getDouble("price"));
                p.setNum(resultSet.getInt("num"));
                p.setUnit(resultSet.getString("unit"));
                list.add(p);
            }
            resultSet.close();
            ps.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    /*
    查询总记录数
     */
    public int findCount()
    {
        int count=0;
        Connection connection=getConnetion();
        String sql="select count(*) from tb_product";
        try {
            Statement statement=connection.createStatement();
            ResultSet resultSet=statement.executeQuery(sql);
            if(resultSet.next())
            {
                count=resultSet.getInt(1);
            }
            resultSet.close();
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }
}

创建名称为FindServelt的类,该类是分页查询商品信息的Servelt对象。代码如下:

package test;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet(name = "FindServlet")
public class FindServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
/*
分别获取分页查询结果集和构造分页条对象
分页查询结果集调用ProductDao的find方法,并传递所查询的页码就可以获取
分页条对象时JSP页面中的分页条,用于显示商品信息的条码。在程序中通过创建页码的超链接,组合字符串进行构造
 */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int currPage=1;
        if(request.getParameter("page")!=null)
        {
            currPage=Integer.parseInt(request.getParameter("page"));
        }
        ProductDao dao=new ProductDao();
        List<Product> list=dao.find(currPage);
        request.setAttribute("list",list);
        int pages;
        int count=dao.findCount();
        if(count% Product.PAGE_SIZE==0)
        {
            pages=count/ Product.PAGE_SIZE;
        }
        else {
            pages=count/ Product.PAGE_SIZE+1;
        }
        StringBuffer sb=new StringBuffer();
        for(int i=1;i<=pages;i++)
        {
            if(i==currPage)
            {
                sb.append("["+i+"]");
            }
            else
            {
                sb.append("<a href='FindServelt?page="+i+"'>"+i+"</a>");
            }
            sb.append(" ");
        }
        request.setAttribute("bar",sb.toString());
        request.getRequestDispatcher("product_list.jsp").forward(request,response);
    }
}

创建product_list.jsp页面,该页面通过获取结果集List与分页条来显示商品信息数据。代码如下:

<%@ page import="test.Product" %>
<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2019/11/16
  Time: 11:08
  To change this template use File | Settings | File Templates.
--%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>商品信息</title>
</head>
<body>
<table align="center" width="450" border="1">
    <tr>
        <td align="center" colspan="5">
            <h2>所有商品信息</h2>
        </td>
    </tr>
    <tr align="center">
        <td><b>ID</b></td>
        <td><b>商品名称</b></td>
        <td><b>价格</b></td>
        <td><b>数量</b></td>
        <td><b>单位</b></td>
    </tr>
    <%
        List<Product> list=(List<Product>)request.getAttribute("list");
        for(Product p:list){
    %>
    <tr align="center">
        <td><%=p.getId()%></td>
        <td><%=p.getName()%></td>
        <td><%=p.getPrice()%></td>
        <td><%=p.getNum()%></td>
        <td><%=p.getUnit()%></td>
    </tr>
    <%
        }
    %>
    <tr>
        <td align="center" colspan="5">
            <%=request.getAttribute("bar")%>
        </td>
    </tr>
</table>
</body>
</html>

编写程序中的主页面index.jsp,在该页面中编写分页查询商品信息的超链接,指向FindServelt。代码如下:

<%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title></title>
  </head>
  <body>
<a href="FindServelt">查看所有商品信息</a>
  </body>
</html>

完成代码的编写,部署运行项目,打开index页面:

                                          

点击页面的超链接,显示结果为:

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值