本文介绍如何使用 Java API 访问 HBase 实例。
多可用区实例仅支持通过 ZK 私网连接地址访问,且不支持开启登录认证。
获取 HBase 实例的 ZK 连接地址。具体操作步骤,请参见查看连接地址。
配置 ZK 地址连接 HBase 实例。
<dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>2.2.5</version> </dependency>
说明
您需要修改代码中 config.set
的如下配置:
zkEndpoint:Port
。HBase 实例 ID
。您可以在 HBase 控制台的实例列表页找到并复制目标实例 ID。通过 ZK 私网连接地址访问实例
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes; // Class that has nothing but a main. // Does a Put, Get and a Scan against an hbase table. // The API described here is since HBase 1.0. public class MyLittleHBaseClient { public static void main(String[] args) throws IOException { // You need a configuration object to tell the client where to connect. // When you create a HBaseConfiguration, it reads in whatever you've set // into your hbase-site.xml and in hbase-default.xml, as long as these can // be found on the CLASSPATH Configuration config = HBaseConfiguration.create(); // Next you need a Connection to the cluster. Create one. When done with it, // close it. A try/finally is a good way to ensure it gets closed or use // the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html // // Connections are heavyweight. Create one once and keep it around. From a Connection // you get a Table instance to access Tables, an Admin instance to administer the cluster, // and RegionLocator to find where regions are out on the cluster. As opposed to Connections, // Table, Admin and RegionLocator instances are lightweight; create as you need them and then // close when done. // config.set(HConstants.ZOOKEEPER_QUORUM, <zkEndpoint:Port>); config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase/<HBase 实例 ID>"); //HConstants.ZOOKEEPER_ZNO Connection connection = ConnectionFactory.createConnection(config); try { // The below instantiates a Table object that connects you to the "myLittleHBaseTable" table // (TableName.valueOf turns String into a TableName instance). // When done with it, close it (Should start a try/finally after this creation so it gets // closed for sure the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable")); try { // To add to a row, use Put. A Put constructor takes the name of the row // you want to insert into as a byte array. In HBase, the Bytes class has // utility for converting all kinds of java types to byte arrays. In the // below, we are converting the String "myLittleRow" into a byte array to // use as a row key for our update. Once you have a Put instance, you can // adorn it by setting the names of columns you want to update on the row, // the timestamp to use in your update, etc. If no timestamp, the server // applies current time to the edits. Put p = new Put(Bytes.toBytes("myLittleRow")); // To set the value you'd like to update in the row 'myLittleRow', specify // the column family, column qualifier, and value of the table cell you'd // like to update. The column family must already exist in your table // schema. The qualifier can be anything. All must be specified as byte // arrays as hbase is all about byte arrays. Lets pretend the table // 'myLittleHBaseTable' was created with a family 'myLittleFamily'. KeyValue kv = new KeyValue(p.getRow(), Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"), Bytes.toBytes("Some Value")); p.add(kv); // Once you've adorned your Put instance with all the updates you want to // make, to commit it do the following (The HTable#put method takes the // Put instance you've been building and pushes the changes you made into // hbase) table.put(p); // Now, to retrieve the data we just wrote. The values that come back are // Result instances. Generally, a Result is an object that will package up // the hbase return into the form you find most palatable. Get g = new Get(Bytes.toBytes("myLittleRow")); Result r = table.get(g); byte [] value = r.getValue(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier")); // If we convert the value bytes, we should get back 'Some Value', the // value we inserted at this location. String valueStr = Bytes.toString(value); System.out.println("GET: " + valueStr); // Sometimes, you won't know the row you're looking for. In this case, you // use a Scanner. This will give you cursor-like interface to the contents // of the table. To set up a Scanner, do like you did above making a Put // and a Get, create a Scan. Adorn it with column names, etc. Scan s = new Scan(); s.addColumn(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier")); ResultScanner scanner = table.getScanner(s); try { // Scanners return Result instances. // Now, for the actual iteration. One way is to use a while loop like so: for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { // print out the row we found and the columns we were looking for System.out.println("Found row: " + rr); } // The other approach is to use a foreach loop. Scanners are iterable! // for (Result rr : scanner) { // System.out.println("Found row: " + rr); // } } finally { // Make sure you close your scanners when you are done! // Thats why we have it inside a try/finally clause scanner.close(); } // Close your table and cluster connection. } finally { if (table != null) table.close(); } } finally { connection.close(); } } }
通过 ZK 公网连接地址访问实例
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes; // Class that has nothing but a main. // Does a Put, Get and a Scan against an hbase table. // The API described here is since HBase 1.0. public class MyLittleHBaseClient { public static void main(String[] args) throws IOException { // You need a configuration object to tell the client where to connect. // When you create a HBaseConfiguration, it reads in whatever you've set // into your hbase-site.xml and in hbase-default.xml, as long as these can // be found on the CLASSPATH Configuration config = HBaseConfiguration.create(); // Next you need a Connection to the cluster. Create one. When done with it, // close it. A try/finally is a good way to ensure it gets closed or use // the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html // // Connections are heavyweight. Create one once and keep it around. From a Connection // you get a Table instance to access Tables, an Admin instance to administer the cluster, // and RegionLocator to find where regions are out on the cluster. As opposed to Connections, // Table, Admin and RegionLocator instances are lightweight; create as you need them and then // close when done. // config.set(HConstants.ZOOKEEPER_QUORUM, <zkEndpoint:Port>); config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase/<HBase 实例 ID>"); config.set("zookeeper.znode.metaserver", "public-meta-region-server"); config.set("zookeeper.znode.master", "public-master"); //HConstants.ZOOKEEPER_ZNO Connection connection = ConnectionFactory.createConnection(config); try { // The below instantiates a Table object that connects you to the "myLittleHBaseTable" table // (TableName.valueOf turns String into a TableName instance). // When done with it, close it (Should start a try/finally after this creation so it gets // closed for sure the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable")); try { // To add to a row, use Put. A Put constructor takes the name of the row // you want to insert into as a byte array. In HBase, the Bytes class has // utility for converting all kinds of java types to byte arrays. In the // below, we are converting the String "myLittleRow" into a byte array to // use as a row key for our update. Once you have a Put instance, you can // adorn it by setting the names of columns you want to update on the row, // the timestamp to use in your update, etc. If no timestamp, the server // applies current time to the edits. Put p = new Put(Bytes.toBytes("myLittleRow")); // To set the value you'd like to update in the row 'myLittleRow', specify // the column family, column qualifier, and value of the table cell you'd // like to update. The column family must already exist in your table // schema. The qualifier can be anything. All must be specified as byte // arrays as hbase is all about byte arrays. Lets pretend the table // 'myLittleHBaseTable' was created with a family 'myLittleFamily'. KeyValue kv = new KeyValue(p.getRow(), Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier"), Bytes.toBytes("Some Value")); p.add(kv); // Once you've adorned your Put instance with all the updates you want to // make, to commit it do the following (The HTable#put method takes the // Put instance you've been building and pushes the changes you made into // hbase) table.put(p); // Now, to retrieve the data we just wrote. The values that come back are // Result instances. Generally, a Result is an object that will package up // the hbase return into the form you find most palatable. Get g = new Get(Bytes.toBytes("myLittleRow")); Result r = table.get(g); byte [] value = r.getValue(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier")); // If we convert the value bytes, we should get back 'Some Value', the // value we inserted at this location. String valueStr = Bytes.toString(value); System.out.println("GET: " + valueStr); // Sometimes, you won't know the row you're looking for. In this case, you // use a Scanner. This will give you cursor-like interface to the contents // of the table. To set up a Scanner, do like you did above making a Put // and a Get, create a Scan. Adorn it with column names, etc. Scan s = new Scan(); s.addColumn(Bytes.toBytes("myLittleFamily"), Bytes.toBytes("someQualifier")); ResultScanner scanner = table.getScanner(s); try { // Scanners return Result instances. // Now, for the actual iteration. One way is to use a while loop like so: for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { // print out the row we found and the columns we were looking for System.out.println("Found row: " + rr); } // The other approach is to use a foreach loop. Scanners are iterable! // for (Result rr : scanner) { // System.out.println("Found row: " + rr); // } } finally { // Make sure you close your scanners when you are done! // Thats why we have it inside a try/finally clause scanner.close(); } // Close your table and cluster connection. } finally { if (table != null) table.close(); } } finally { connection.close(); } } }
获取 HBase 实例的 ZK 连接地址。具体操作步骤,请参见查看连接地址。
配置 ZK 地址连接 HBase 实例。
在本地业务环境的 Maven 中添加如下配置:
<repositories> <repository> <id>bytedance-hbase</id> <url>https://artifacts-cn-beijing.volces.com/repository/hbase/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <!-- 指定项目依赖 --> <dependencies> <!-- HBase客户端 --> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>2.3.8-ha.1</version> <exclusions> <exclusion> <groupId>com.volcengine.cloudfs</groupId> <artifactId>cloudfs-hadoop</artifactId> </exclusion> </exclusions> </dependency> <!-- HBase 客户端HA相关依赖 --> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-connector</artifactId> <version>2.3.8-ha.1</version> <exclusions> <exclusion> <groupId>com.volcengine.cloudfs</groupId> <artifactId>cloudfs-hadoop</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
在本地业务代码中增加如下配置文件来访问实例。
说明
您需要修改代码中 config.set
的如下配置:
用步骤 1 中获取的 ZK 地址替换代码中的 zkEndpoint:Port
。
用目标 HBase 实例 ID 替换代码中的 HBase 实例 ID
。您可以在 HBase 控制台的实例列表页找到并复制目标实例 ID。
通过 ZK 私网连接地址访问实例
package com.tian.test; import java.io.IOException; import java.nio.charset.StandardCharsets; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.haclient.HBaseHAClientConstants; import org.apache.hadoop.hbase.util.Bytes; // Class that has nothing but a main. // Does a Put, Get and a Scan against an hbase table. // The API described here is since HBase 1.0. public class MyLittleHBaseClient { public static void main(String[] args) throws IOException, InterruptedException { // You need a configuration object to tell the client where to connect. // When you create a HBaseConfiguration, it reads in whatever you've set // into your hbase-site.xml and in hbase-default.xml, as long as these can // be found on the CLASSPATH Configuration config = HBaseConfiguration.create(); // Next you need a Connection to the cluster. Create one. When done with it, // close it. A try/finally is a good way to ensure it gets closed or use // the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html // // Connections are heavyweight. Create one once and keep it around. From a Connection // you get a Table instance to access Tables, an Admin instance to administer the cluster, // and RegionLocator to find where regions are out on the cluster. As opposed to Connections, // Table, Admin and RegionLocator instances are lightweight; create as you need them and then // close when done. // config.set(HConstants.ZOOKEEPER_QUORUM, "hbha-xxxx-zk.config.ivolces.com:2181"); config.set(HBaseHAClientConstants.HACLIENT_CLUSTER_ID, "hbha-xxxx"); config.set("hbase.client.connection.impl", "org.apache.hadoop.hbase.haclient.HBaseMultiClusterConnectionImpl"); //HConstants.ZOOKEEPER_ZNO Connection connection = ConnectionFactory.createConnection(config); TableName test_ha_table = TableName.valueOf("test_ha_table"); try { HTableDescriptor htd = new HTableDescriptor(test_ha_table); final HColumnDescriptor col = new HColumnDescriptor(Bytes.toBytes("f")); htd.addFamily(col); HBaseMultiAdmin admin = (HBaseMultiAdmin)connection.getAdmin(); if (!admin.tableExists(test_ha_table)) { admin.createTable(htd); } // The below instantiates a Table object that connects you to the "myLittleHBaseTable" table // (TableName.valueOf turns String into a TableName instance). // When done with it, close it (Should start a try/finally after this creation so it gets // closed for sure the jdk7 idiom, try-with-resources: see // https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) Table table = connection.getTable(test_ha_table); // //Table table = connection.getTable(TableName.valueOf("myLittleHBaseTable_s")); try { // // // To add to a row, use Put. A Put constructor takes the name of the row // // you want to insert into as a byte array. In HBase, the Bytes class has // // utility for converting all kinds of java types to byte arrays. In the // // below, we are converting the String "myLittleRow" into a byte array to // // use as a row key for our update. Once you have a Put instance, you can // // adorn it by setting the names of columns you want to update on the row, // // the timestamp to use in your update, etc. If no timestamp, the server // // applies current time to the edits. for (int i = 0; i < Integer.MAX_VALUE; i++) { writeAndRead(table,"myLittleRow" + i, "myLittleRowValue" + i); // 3s 做一次读写 Thread.sleep(3000); } // Close your table and cluster connection. } finally { if (table != null) table.close(); } } finally { connection.close(); } } private static void writeAndRead(Table table, String rowKey, String rowValue) throws IOException { Put p = new Put(Bytes.toBytes(rowKey)); // // // To set the value you'd like to update in the row 'myLittleRow', specify // // the column family, column qualifier, and value of the table cell you'd // // like to update. The column family must already exist in your table // // schema. The qualifier can be anything. All must be specified as byte // // arrays as hbase is all about byte arrays. Lets pretend the table // // 'myLittleHBaseTable' was created with a family 'myLittleFamily'. KeyValue kv = new KeyValue(p.getRow(), Bytes.toBytes("f"), Bytes.toBytes("someQualifier"), Bytes.toBytes(rowValue)); p.add(kv); // // // Once you've adorned your Put instance with all the updates you want to // // make, to commit it do the following (The HTable#put method takes the // // Put instance you've been building and pushes the changes you made into // // hbase) table.put(p); // Now, to retrieve the data we just wrote. The values that come back are // Result instances. Generally, a Result is an object that will package up // the hbase return into the form you find most palatable. Get g = new Get(Bytes.toBytes(rowKey)); Result r = table.get(g); byte [] value = r.getValue(Bytes.toBytes("f"), Bytes.toBytes("someQualifier")); // If we convert the value bytes, we should get back 'Some Value', the // value we inserted at this location. String valueStr = Bytes.toString(value); System.out.println("rowKey:" +new String(g.getRow()) + "GET: " + valueStr + "origin:" + rowValue); // Sometimes, you won't know the row you're looking for. In this case, you // use a Scanner. This will give you cursor-like interface to the contents // of the table. To set up a Scanner, do like you did above making a Put // and a Get, create a Scan. Adorn it with column names, etc. Scan s = new Scan().withStartRow(Bytes.toBytes(rowKey)); s.addColumn(Bytes.toBytes("f"), Bytes.toBytes("someQualifier")); ResultScanner scanner = table.getScanner(s); try { // Scanners return Result instances. // Now, for the actual iteration. One way is to use a while loop like so: for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { // print out the row we found and the columns we were looking for System.out.println("Found row: " + rr); } // The other approach is to use a foreach loop. Scanners are iterable! // for (Result rr : scanner) { // System.out.println("Found row: " + rr); // } } finally { // Make sure you close your scanners when you are done! // Thats why we have it inside a try/finally clause scanner.close(); } } }
获取 HBase 实例的 ZK 连接地址。具体操作步骤,请参见查看连接地址。
修改本地业务环境中的 pom.xml
文件。
<!-- 指定 maven 仓库 --> <repositories> <repository> <id>bytedance-hbase</id> <url>https://artifacts-cn-beijing.volces.com/repository/hbase/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <!-- 指定项目依赖 --> <dependencies> <!-- HBase客户端 --> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>2.3.7</version> </dependency> <!-- HBase 客户端认证相关依赖 --> <dependency> <groupId>com.volcengine</groupId> <artifactId>hbase-security</artifactId> <version>2.3.8</version> </dependency> </dependencies>
在业务代码中设置如下配置来连接实例。
说明
您需要修改代码中的如下信息:
使用创建的数据库账号和密码替换代码中的 <username>
和 <password>
。
用步骤 1 中获取的 ZK 地址替换代码中的 <zkEndpoint:Port>
。
用目标 HBase 实例 ID 替换代码中的 <HBase 实例 ID>
。您可以在 HBase 控制台的实例列表页找到并复制目标实例 ID。
使用 ZK 私网连接地址访问实例
public class MyLittleHBaseClient { public static void main(String[] args) throws IOException { Iterator i$ = ServiceLoader.load(TokenIdentifier.class).iterator(); while(i$.hasNext()) { TokenIdentifier id = (TokenIdentifier)i$.next(); System.out.println(id.getKind() + "|" + id.getClass()); } Configuration config = HBaseConfiguration.create(); // hbase 连接相关配置 config.set(HConstants.ZOOKEEPER_QUORUM, "<zkEndpoint:Port>"); config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase/<HBase 实例 ID>"); config.set("zookeeper.znode.acl.parent", "/hbase/<HBase 实例 ID>"); // hbase 鉴权相关配置 config.setStrings(SaslClientAuthenticationProviders.EXTRA_PROVIDERS_KEY, PlainSaslClientAuthenticationProvider.class.getName()); config.set(SaslClientAuthenticationProviders.SELECTOR_KEY, AuthProviderSelector.class.getName()); config.set("hbase.client.userprovider.class", BytedanceUserProvider.class.getName()); config.set(HBASE_SECURITY_CONF_KEY, BytedanceUserProvider.BYTE_DANCE_AUTHENTICATION); config.set(BytedanceUserProvider.HBASE_CLIENT_USERNAME, "<username>"); config.set(BytedanceUserProvider.HBASE_CLIENT_PASSWORD, "<password>"); Connection connection = ConnectionFactory.createConnection(config); try { Table table = connection.getTable(TableName.valueOf("t1")); try { Put p = new Put(Bytes.toBytes("myLittleRow")); KeyValue kv = new KeyValue(p.getRow(), Bytes.toBytes("f1"), Bytes.toBytes("someQualifier"), Bytes.toBytes("Some Value")); p.add(kv); table.put(p); Get g = new Get(Bytes.toBytes("myLittleRow")); Result r = table.get(g); byte [] value = r.getValue(Bytes.toBytes("f1"), Bytes.toBytes("someQualifier")); String valueStr = Bytes.toString(value); System.out.println("GET: " + valueStr); Scan s = new Scan(); s.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("someQualifier")); ResultScanner scanner = table.getScanner(s); try { for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { // print out the row we found and the columns we were looking for System.out.println("Found row: " + rr); } } finally { scanner.close(); } } finally { if (table != null) table.close(); } } finally { connection.close(); } } }
使用 ZK 公网连接地址访问实例
public class MyLittleHBaseClient { public static void main(String[] args) throws IOException { Iterator i$ = ServiceLoader.load(TokenIdentifier.class).iterator(); while(i$.hasNext()) { TokenIdentifier id = (TokenIdentifier)i$.next(); System.out.println(id.getKind() + "|" + id.getClass()); } Configuration config = HBaseConfiguration.create(); // hbase 连接相关配置 config.set(HConstants.ZOOKEEPER_QUORUM, "<zkEndpoint:Port>"); config.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/hbase/<HBase 实例 ID>"); config.set("zookeeper.znode.acl.parent", "/hbase/<HBase 实例 ID>"); config.set("zookeeper.znode.metaserver", "public-meta-region-server"); config.set("zookeeper.znode.master", "public-master"); // hbase 鉴权相关配置 config.setStrings(SaslClientAuthenticationProviders.EXTRA_PROVIDERS_KEY, PlainSaslClientAuthenticationProvider.class.getName()); config.set(SaslClientAuthenticationProviders.SELECTOR_KEY, AuthProviderSelector.class.getName()); config.set("hbase.client.userprovider.class", BytedanceUserProvider.class.getName()); config.set(HBASE_SECURITY_CONF_KEY, BytedanceUserProvider.BYTE_DANCE_AUTHENTICATION); config.set(BytedanceUserProvider.HBASE_CLIENT_USERNAME, "<username>"); config.set(BytedanceUserProvider.HBASE_CLIENT_PASSWORD, "<password>"); Connection connection = ConnectionFactory.createConnection(config); try { Table table = connection.getTable(TableName.valueOf("t1")); try { Put p = new Put(Bytes.toBytes("myLittleRow")); KeyValue kv = new KeyValue(p.getRow(), Bytes.toBytes("f1"), Bytes.toBytes("someQualifier"), Bytes.toBytes("Some Value")); p.add(kv); table.put(p); Get g = new Get(Bytes.toBytes("myLittleRow")); Result r = table.get(g); byte [] value = r.getValue(Bytes.toBytes("f1"), Bytes.toBytes("someQualifier")); String valueStr = Bytes.toString(value); System.out.println("GET: " + valueStr); Scan s = new Scan(); s.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("someQualifier")); ResultScanner scanner = table.getScanner(s); try { for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { // print out the row we found and the columns we were looking for System.out.println("Found row: " + rr); } } finally { scanner.close(); } } finally { if (table != null) table.close(); } } finally { connection.close(); } } }