The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
A FilteredRowSet
object lets you cut down the number of rows that are visible in a RowSet
object so that you can work with only the data that is relevant to what you are doing. FilteredRowSet
对象可以减少RowSet
对象中可见的行数,以便只处理与所做操作相关的数据。You decide what limits you want to set on your data (how you want to "filter" the data) and apply that filter to a 您可以决定要对数据设置什么限制(如何“筛选”数据),并将该过滤器应用于FilteredRowSet
object. FilteredRowSet
对象。In other words, the 换句话说,FilteredRowSet
object makes visible only the rows of data that fit within the limits you set. FilteredRowSet
对象仅使符合您设置的限制的数据行可见。A JdbcRowSet
object, which always has a connection to its data source, can do this filtering with a query to the data source that selects only the columns and rows you want to see. JdbcRowSet
对象始终与其数据源有连接,它可以通过对数据源的查询进行筛选,该查询只选择要查看的列和行。The query's 查询的WHERE
clause defines the filtering criteria. WHERE
子句定义筛选条件。A FilteredRowSet
object provides a way for a disconnected RowSet
object to do this filtering without having to execute a query on the data source, thus avoiding having to get a connection to the data source and sending queries to it.FilteredRowSet
对象为断开连接的RowSet
对象提供了一种进行此筛选的方法,而无需对数据源执行查询,从而避免了必须获得到数据源的连接并向其发送查询。
For example, assume that the Coffee Break chain of coffee houses has grown to hundreds of stores throughout the United States of America, and all of them are listed in a table called 例如,假设咖啡屋的咖啡休息连锁店已发展到美国各地的数百家商店,所有这些商店都列在一个名为COFFEE_HOUSES
. COFFEE_HOUSES
的表格中。The owner wants to measure the success of only the stores in California with a coffee house comparison application that does not require a persistent connection to the database system. 店主只想衡量加利福尼亚州的商店是否成功,这些商店有一个咖啡馆比较应用程序,不需要与数据库系统建立持久连接。This comparison will look at the profitability of selling merchandise versus selling coffee drinks plus various other measures of success, and it will rank California stores by coffee drink sales, merchandise sales, and total sales. 这一比较将着眼于销售商品与销售咖啡饮料以及其他各种成功衡量标准的盈利能力,并将根据咖啡饮料销售额、商品销售额和总销售额对加利福尼亚州的商店进行排名。Because the table 由于表COFFEE_HOUSES
has hundreds of rows, these comparisons will be faster and easier if the amount of data being searched is cut down to only those rows where the value in the column STORE_ID
indicates California.COFFEE_HOUSES
有数百行,如果搜索的数据量减少到只有那些列STORE_ID
中的值表示California的行,这些比较将更快更容易。
This is exactly the kind of problem that a 这正是FilteredRowSet
object addresses by providing the following capabilities:FilteredRowSet
对象通过提供以下功能解决的问题:
The following topics are covered:涵盖以下主题:
To set the criteria for which rows in a 要设置FilteredRowSet
object will be visible, you define a class that implements the Predicate
interface. FilteredRowSet
对象中哪些行可见的条件,请定义一个实现Predicate
接口的类。An object created with this class is initialized with the following:使用此类创建的对象将通过以下方式初始化:
Note that the range of values is inclusive, meaning that a value at the boundary is included in the range. 请注意,值的范围是包含的,这意味着边界处的值包含在范围中。For example, if the range has a high of 100 and a low of 50, a value of 50 is considered to be within the range. 例如,如果范围的高值为100,低值为50,则认为值50在范围内。A value of 49 is not. Likewise, 100 is within the range, but 101 is not.值49不适用。同样,100在范围内,但101不在范围内。
In line with the scenario where the owner wants to compare California stores, an implementation of the Predicate
interface that filters for Coffee Break coffee houses located in California must be written. There is no one right way to do this, which means there is a lot of latitude in how the implementation is written. For example, you could name the class and its members whatever you want and implement a constructor and the three evaluate methods in any way that accomplishes the desired results.
The table listing all of the coffee houses, named COFFEE_HOUSES
, has hundreds of rows. To make things more manageable, this example uses a table with far fewer rows, which is enough to demonstrate how filtering is done.
A value in the column STORE_ID
is an int
value that indicates, among other things, the state in which the coffee house is located. A value beginning with 10, for example, means that the state is California. STORE_ID
values beginning with 32 indicate Oregon, and those beginning with 33 indicate the state of Washington.
The following class StateFilter
implements the Predicate
interface:
public class StateFilter implements Predicate { private int lo; private int hi; private String colName = null; private int colNumber = -1; public StateFilter(int lo, int hi, int colNumber) { this.lo = lo; this.hi = hi; this.colNumber = colNumber; } public StateFilter(int lo, int hi, String colName) { this.lo = lo; this.hi = hi; this.colName = colName; } public boolean evaluate(Object value, String columnName) { boolean evaluation = true; if (columnName.equalsIgnoreCase(this.colName)) { int columnValue = ((Integer)value).intValue(); if ((columnValue >= this.lo) && (columnValue <= this.hi)) { evaluation = true; } else { evaluation = false; } } return evaluation; } public boolean evaluate(Object value, int columnNumber) { boolean evaluation = true; if (this.colNumber == columnNumber) { int columnValue = ((Integer)value).intValue(); if ((columnValue >= this.lo) && (columnValue <= this.hi)) { evaluation = true; } else { evaluation = false; } } return evaluation; } public boolean evaluate(RowSet rs) { CachedRowSet frs = (CachedRowSet)rs; boolean evaluation = false; try { int columnValue = -1; if (this.colNumber > 0) { columnValue = frs.getInt(this.colNumber); } else if (this.colName != null) { columnValue = frs.getInt(this.colName); } else { return false; } if ((columnValue >= this.lo) && (columnValue <= this.hi)) { evaluation = true; } } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); return false; } catch (NullPointerException npe) { System.err.println("NullPointerException caught"); return false; } return evaluation; } }
This is a very simple implementation that checks the value in the column specified by either colName
or colNumber
to see if it is in the range of lo
to hi
, inclusive. The following line of code, from FilteredRowSetSample
, creates a filter that allows only the rows where the STORE_ID
column value indicates a value between 10000 and 10999, which indicates a California location:
StateFilter myStateFilter = new StateFilter(10000, 10999, 1);
Note that the StateFilter
class just defined applies to one column. It is possible to have it apply to two or more columns by making each of the parameters arrays instead of single values. For example, the constructor for a Filter
object could look like the following:
public Filter2(Object [] lo, Object [] hi, Object [] colNumber) { this.lo = lo; this.hi = hi; this.colNumber = colNumber; }
The first element in the colNumber
object gives the first column in which the value will be checked against the first element in lo
and the first element in hi
. The value in the second column indicated by colNumber
will be checked against the second elements in lo
and hi
, and so on. Therefore, the number of elements in the three arrays should be the same. The following code is what an implementation of the method evaluate(RowSet rs)
might look like for a Filter2
object, in which the parameters are arrays:
public boolean evaluate(RowSet rs) { CachedRowSet crs = (CachedRowSet)rs; boolean bool1; boolean bool2; for (int i = 0; i < colNumber.length; i++) { if ((rs.getObject(colNumber[i] >= lo [i]) && (rs.getObject(colNumber[i] <= hi[i]) { bool1 = true; } else { bool2 = true; } if (bool2) { return false; } else { return true; } } }
The advantage of using a Filter2
implementation is that you can use parameters of any Object
type and can check one column or multiple columns without having to write another implementation. However, you must pass an Object
type, which means that you must convert a primitive type to its Object
type. For example, if you use an int
value for lo
and hi
, you must convert the int
value to an Integer
object before passing it to the constructor. String
objects are already Object
types, so you do not have to convert them.
Use an instance of RowSetFactory
, which is created from the class RowSetProvider
, to create a FilteredRowSet
object. The following is from FilteredRowSetSample
:
RowSetFactory factory = RowSetProvider.newFactory(); try (FilteredRowSet frs = factory.createFilteredRowSet()) { // ...
Like other disconnected RowSet
objects, the frs
object must populate itself with data from a tabular data source, which is a relational database in the reference implementation. The following code fragment from FilteredRowSetSample
sets the properties necessary to connect to a database to execute its command. Note that this code uses the DriverManager
class to make a connection, which is done for convenience. Usually, it is better to use a DataSource
object that has been registered with a naming service that implements the Java Naming and Directory Interface (JNDI). The following example is from FilteredRowSetSample
:
RowSetFactory factory = RowSetProvider.newFactory(); try (FilteredRowSet frs = factory.createFilteredRowSet()){ frs.setCommand("SELECT * FROM COFFEE_HOUSES"); frs.setUsername(settings.userName); frs.setPassword(settings.password); frs.setUrl(settings.urlString); frs.execute(); // ...
The following line of code populates the frs
object with the data stored in the COFFEE_HOUSE
table:
frs.execute();
The method execute
does all kinds of things in the background by calling on the RowSetReader
object for frs
, which creates a connection, executes the command for frs
, populates frs
with the data from the ResultSet
object that is produced, and closes the connection. Note that if the table COFFEE_HOUSES
had more rows than the frs
object could hold in memory at one time, the CachedRowSet
paging methods would have been used.
In the scenario, the Coffee Break owner would have done the preceding tasks in the office and then imported or downloaded the information stored in the frs
object to the coffee house comparison application. From now on, the frs
object will operate independently without the benefit of a connection to the data source.
Now that the FilteredRowSet
object frs
contains the list of Coffee Break establishments, you can set selection criteria for narrowing down the number of rows in the frs
object that are visible.
The following line of code uses the StateFilter
class defined previously to create the object myStateFilter
, which checks the column STORE_ID
to determine which stores are in California (a store is in California if its ID number is between 10000 and 10999, inclusive):
StateFilter myStateFilter = new StateFilter(10000, 10999, 1);
The following line sets myStateFilter
as the filter for frs
.
frs.setFilter(myStateFilter);
To do the actual filtering, you call the method next
, which in the reference implementation calls the appropriate version of the Predicate.evaluate
method that you have implemented previously.
If the return value is true
, the row will be visible; if the return value is false
, the row will not be visible.
You set multiple filters serially. The first time you call the method setFilter
and pass it a Predicate
object, you have applied the filtering criteria in that filter. After calling the method next
on each row, which makes visible only those rows that satisfy the filter, you can call setFilter
again, passing it a different Predicate
object. Even though only one filter is set at a time, the effect is that both filters apply cumulatively.
For example, the owner has retrieved a list of the Coffee Break stores in California by setting stateFilter
as the Predicate
object for frs
. Now the owner wants to compare the stores in two California cities, San Francisco (SF in the table COFFEE_HOUSES
) and Los Angeles (LA in the table). The first thing to do is to write a Predicate
implementation that filters for stores in either SF or LA:
public class CityFilter implements Predicate { private String[] cities; private String colName = null; private int colNumber = -1; public CityFilter(String[] citiesArg, String colNameArg) { this.cities = citiesArg; this.colNumber = -1; this.colName = colNameArg; } public CityFilter(String[] citiesArg, int colNumberArg) { this.cities = citiesArg; this.colNumber = colNumberArg; this.colName = null; } public boolean evaluate Object valueArg, String colNameArg) { if (colNameArg.equalsIgnoreCase(this.colName)) { for (int i = 0; i < this.cities.length; i++) { if (this.cities[i].equalsIgnoreCase((String)valueArg)) { return true; } } } return false; } public boolean evaluate(Object valueArg, int colNumberArg) { if (colNumberArg == this.colNumber) { for (int i = 0; i < this.cities.length; i++) { if (this.cities[i].equalsIgnoreCase((String)valueArg)) { return true; } } } return false; } public boolean evaluate(RowSet rs) { if (rs == null) return false; try { for (int i = 0; i < this.cities.length; i++) { String cityName = null; if (this.colNumber > 0) { cityName = (String)rs.getObject(this.colNumber); } else if (this.colName != null) { cityName = (String)rs.getObject(this.colName); } else { return false; } if (cityName.equalsIgnoreCase(cities[i])) { return true; } } } catch (SQLException e) { return false; } return false; } }
The following code fragment from FilteredRowSetSample
sets the new filter and iterates through the rows in frs
, printing out the rows where the CITY
column contains either SF or LA. Note that frs
currently contains only rows where the store is in California, so the criteria of the Predicate
object state
are still in effect when the filter is changed to another Predicate
object. The code that follows sets the filter to the CityFilter
object city
. The CityFilter
implementation uses arrays as parameters to the constructors to illustrate how that can be done:
public void testFilteredRowSet() throws SQLException { StateFilter myStateFilter = new StateFilter(10000, 10999, 1); String[] cityArray = { "SF", "LA" }; CityFilter myCityFilter = new CityFilter(cityArray, 2); RowSetFactory factory = RowSetProvider.newFactory(); try (FilteredRowSet frs = factory.createFilteredRowSet()){ frs.setCommand("SELECT * FROM COFFEE_HOUSES"); frs.setUsername(settings.userName); frs.setPassword(settings.password); frs.setUrl(settings.urlString); frs.execute(); System.out.println("\nBefore filter:"); FilteredRowSetSample.viewTable(this.con); System.out.println("\nSetting state filter:"); frs.beforeFirst(); frs.setFilter(myStateFilter); this.viewFilteredRowSet(frs); System.out.println("\nSetting city filter:"); frs.beforeFirst(); frs.setFilter(myCityFilter); this.viewFilteredRowSet(frs); } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); } }
The output should contain a row for each store that is in San Francisco, California or Los Angeles, California. If there were a row in which the CITY
column contained LA and the STORE_ID
column contained 40003, it would not be included in the list because it had already been filtered out when the filter was set to state
. (40003 is not in the range of 10000 to 10999.)
You can make a change to a FilteredRowSet
object but only if that change does not violate any of the filtering criteria currently in effect. For example, you can insert a new row or change one or more values in an existing row if the new value or values are within the filtering criteria.
Assume that two new Coffee Break coffee houses have just opened and the owner wants to add them to the list of all coffee houses. If a row to be inserted does not meet the cumulative filtering criteria in effect, it will be blocked from being added.
The current state of the frs
object is that the StateFilter
object was set and then the CityFilter
object was set. As a result, frs
currently makes visible only those rows that satisfy the criteria for both filters. And, equally important, you cannot add a row to the frs
object unless it satisfies the criteria for both filters. The following code fragment attempts to insert two new rows into the frs
object, one row in which the values in the STORE_ID
and CITY
columns both meet the criteria, and one row in which the value in STORE_ID
does not pass the filter but the value in the CITY
column does:
frs.moveToInsertRow(); frs.updateInt("STORE_ID", 10101); frs.updateString("CITY", "SF"); frs.updateLong("COF_SALES", 0); frs.updateLong("MERCH_SALES", 0); frs.updateLong("TOTAL_SALES", 0); frs.insertRow(); frs.updateInt("STORE_ID", 33101); frs.updateString("CITY", "SF"); frs.updateLong("COF_SALES", 0); frs.updateLong("MERCH_SALES", 0); frs.updateLong("TOTAL_SALES", 0); frs.insertRow(); frs.moveToCurrentRow();
If you were to iterate through the frs
object using the method next
, you would find a row for the new coffee house in San Francisco, California, but not for the store in San Francisco, Washington.
The owner can add the store in Washington by nullifying the filter. With no filter set, all rows in the frs
object are once more visible, and a store in any location can be added to the list of stores. The following line of code unsets the current filter, effectively nullifying both of the Predicate
implementations previously set on the frs
object.
frs.setFilter(null);
If the owner decides to close down or sell one of the Coffee Break coffee houses, the owner will want to delete it from the COFFEE_HOUSES
table. The owner can delete the row for the under-performing coffee house as long as the row is visible.
For example, given that the method setFilter
has just been called with the argument null, there is no filter set on the frs
object. This means that all rows are visible and can therefore be deleted. However, after the StateFilter
object myStateFilter
was set, which filtered out any state other than California, only stores located in California could be deleted. When the CityFilter
object myCityFilter
was set for the frs
object, only coffee houses in San Francisco, California or Los Angeles, California could be deleted because they were in the only rows visible.