
# 通过Java连接GeminiDB Cassandra
本小节主要介绍使用Java语言连接GeminiDB Cassandra的基本操作。
#### 前提条件
- 已成功创建GeminiDB Cassandra实例，且实例状态正常。创建GeminiDB Cassandra实例的方法请参见[购买GeminiDB Cassandra实例](https://support.huaweicloud.com/cassandraug-nosql/nosql_02_0002.html)。
- 已创建弹性云服务器，创建弹性云服务器的方法，请参见《弹性云服务器》中"[创建弹性云服务器](https://support.huaweicloud.com/usermanual-ecs/ecs_03_7002.html)"章节。
- 弹性云服务器上已经安装JDK环境。
- 客户端建议使用DataStax 3.11.x版本，目前暂不兼容DataStax 4.x版本。
 
#### 依赖示例
**Maven依赖配置示例**
```
<dependency>
    <groupId>com.datastax.cassandra</groupId>
    <artifactId>cassandra-driver-core</artifactId>
    <version>3.11.5</version>
</dependency>
```
**Gradle依赖配置示例**
```
implementation "com.datastax.cassandra:cassandra-driver-core:3.11.5"
```
#### 操作步骤
1. 获取GeminiDB Cassandra实例的内网IP地址、端口。 
   内网IP地址和端口的获取方法请参见[GeminiDB Cassandra查看IP地址和端口](https://support.huaweicloud.com/cassandraug-nosql/nosql_02_0201.html)。
   
   
2. 登录弹性云服务器，具体操作请参见《弹性云服务器》中"[登录弹性云服务器](https://support.huaweicloud.com/ecs_faq/ecs_faq_0632.html)"。
3. 编辑连接GeminiDB Cassandra实例的代码。 
   ```
   import com.datastax.driver.core.*;
   Cluster cluster = null;
   try {
       cluster = Cluster.builder()                                                   
               .addContactPoint("127.0.0.1")//此处为步骤1中获取到的GeminiDB Cassandra实例的内网IP
               .withPort(8635)        //此处为步骤1中获取到的GeminiDB Cassandra实例的端口                   
               .build();
       Session session = cluster.connect();                                           
       ResultSet rs = session.execute("select release_version from system.local");    
       Row row = rs.one();
       System.out.println(row.getString("release_version"));                          
   } finally {
       if (cluster != null) cluster.close();                                          
   }
   ```
   
   
4. 运行示例代码，确认结果是否正常。
 
#### 相关知识
1. 创建集群实例。 如下代码示例，可以快速创建一个集群实例：
   ```
   Cluster cluster = Cluster.builder()
           .withClusterName("myCluster")   //集群名称可选
           .addContactPoint("127.0.0.1")   // 连接点，业务连接集群时第一次连接的节点IP，可配置多个
           .build();
   ```
   
2. 设置集群配置。 必须要指定的选项是指定连接点（ContactPoint），一般情况下建议配置三个连接点IP，这样当其中一个无法连接时，还可以尝试其他的连接点。
   可以配置的配置项有：
   - AuthProvider：认证方式
   
   - LoadBalancingPolicy：负载均衡的策略。负载均衡策略决定了业务请求到Cassandra集群的负载分流的方式， 建议设置为RoundRobinPolicy，这样保持压力能够随机均匀地落在整个集群中。 可选项有DCAwareRoundRobinPolicy，HostFilterPolicy，LatencyAwarePolicy，RoundRobinPolicy，TokenAwarePolicy和WhiteListPolicy。
     
   
   - Metrics：计量相关
   
   - NettyOptions：Cassandra driver使用Netty实现作为内部异步编程框架，所以暴露了一些options给用户自定义配置。
   
   - QueryOptions：查询相关options，可以设置查询一致性级别、设置fetch_size、设置刷新节点拓扑频率、刷新元数据频率等。
   
   - ReconnectionPolicy：当节点连接断开以后的重连策略，可以指定如下重连策略：
     - ConstantReconnectionPolicy：常数级别的重连策略
     
     - ExponentialReconnectionPolicy：指数递增重连策略
      
   
   - RetryPolicy：重试策略，当请求失败的时候，会根据指定的重试策略进行再次请求，默认为DefaultRetryPolicy。 其他可选的重试策略还有：DowngradingConsistencyRetryPolicy，FallthroughRetryPolicy，IdempotenceAwareRetryPolicy和LoggingRetryPolicy，用户也可以根据业务自行定制重试策略。
     
    
3. Session会话。 正常情况下，session和keyspace是不绑定的，所以在查询的时候是需要指定表的keyspace的，如下所示：
   ```
   Session session = cluster.connect(); 
   session.execute("select * from myKeyspace.myTable where id = 1"); 
   //需要手动指定myKeyspace的keyspace前缀
   ```
   以下是几种绑定keyspace的常见方法：
   - 创建session会话时候指定keyspace
     ```
     Session session = cluster.connect("myKeyspace");
     ```
     
   
   - 查询指定keyspace前缀
     ```
     session.execute("select * from otherKeyspace.otherTable where id = 1");
     ```
     
   
   - session.execute 切换keyspace
     ```
     session.execute("USE myKeyspace");
     ```
     
    
4. CRUD（增删改查）操作
   - 直接使用CQL语句实现增删改查操作
     ```
     //创建表 
     session.execute("CREATE TABLE test (k int,p int,s int ,v int,PRIMARY KEY (k, p));");
     //插入 
     session.execute("INSERT INTO test(k, p, v) VALUES (0, 3, 1);");
     //查询 
     session.execute("SELECT * FROM test;");
     //更新 
     session.execute("UPDATE test SET v=100 where k = 0 and p = 3;")
     //删除 
     session.execute("DELETE FROM test where k = 0 and p = 3;")
     ```
     
   
   - 使用QueryBuilder
     ```
     //查询        
     ResultSet rs = session.execute(
                     QueryBuilder.select("k", "p", "v", "s")
                     .from("keyspace", "test")
                     .where(QueryBuilder.eq("k", 0))
                     .and(QueryBuilder.eq("p", 3));                      
     //插入        
     session.execute(
                     QueryBuilder.insertInto("keyspace", "test")
                     .values(new String[]{"k", "p", "v"},
                             new Object[]{0, 3, 0}));                                
     //更新        
     session.execute(
                     QueryBuilder.update("keyspace", "test")
                     .with(QueryBuilder.set("v", 100))
                     .where(QueryBuilder.eq("k", 0))
                     .and(QueryBuilder.eq("p", 3));                
     //删除
     session.execute(QueryBuilder.delete()
                     .from("keyspace", "test")
                     .where(QueryBuilder.eq("k", 0))
                     .and(QueryBuilder.eq("p", 3))
     ```
     
   
   - 使用PreparedStatement Cassandra也有类似于JDBC那样使用预编译占位符，当使用PreparedStatement的时候，Cassandra server端会解析query语句并且在server端进行缓存，然后返回一个唯一标识符给cassandra driver。
     如下图所示：
     图1原理图   
     ![](https://support.huaweicloud.com/cassandraug-nosql/zh-cn_image_0000001991569670.png "点击放大")
     当用户绑定并且执行编译statement时，cassandra driver只会发送唯一标识符和数据，cassandra server会跳过解析query语句过程。
     图2原理图   
     ![](https://support.huaweicloud.com/cassandraug-nosql/zh-cn_image_0000002028209289.png "点击放大")
     预编译占位符在重复执行大量相同CQL，只是数据不一样的场景性能上有很大优势。
     ```
     private static final String GET_TEST = "select * from test where k = ? and p = ?;"; 
     private static final String INSERT_TEST = "INSERT INTO test(k, p, v) VALUES (?, ?, ?);"; 
     private static final String UPDATE_TEST = "UPDATE test SET v=100 where k = ? and p = ?;"; 
     private static final String DELETE_TEST = "DELETE FROM test where k = ? and p = ?;";
     //查询        
     PreparedStatement prepareStatement = session.prepare(GET_TEST);        
     BoundStatement bindStatement = new BoundStatement(prepareStatement).bind(0, 3);        
     ResultSet rs = session.execute(bindStatement);
      //插入        
     PreparedStatement prepareStatement = session.prepare(INSERT_TEST);        
     BoundStatement bindStatement = new BoundStatement(prepareStatement)
                 .bind(0, 3, 0);        
     session.execute(bindStatement);
     //更新        
     PreparedStatement prepareStatement = session.prepare(UPDATE_TEST);        
     BoundStatement bindStatement = new BoundStatement(prepareStatement)
                 .bind(0， 3);        
     session.execute(bindStatement);                
     //删除        
     PreparedStatement prepareStatement = session.prepare(DELETE_TEST);        
     BoundStatement bindStatement = new BoundStatement(prepareStatement)
                 .bind(0, 3);        
     session.execute(bindStatement);
     ```
     
    
5. BATCH操作 cassandra driver也支持用户批量进行插入操作，可以将多个statement添加进BatchStatement统一执行。
   如下代码示例：
   ```
   Session session = SessionRepository.getSession();                
   //插入        
   BoundStatement insertBind = new BoundStatement(
                   session.prepare("insert into keyspace.test(k, p, v) values(?,?,?);"))
                          .bind(0, 3, 0);        
   //更新        
   BoundStatement updateBind = new BoundStatement(
                   session.prepare("update keyspace.test set v=? where k=? and p=?;"))   
                       .bind(0, 3, 100");        
   //删除        
   BoundStatement deleteBind = new BoundStatement(
                   session.prepare("delete from keyspace.test where k=? and p=?;"))
                          .bind(1, 3);        
   // GeminiDB Cassandra只支持unlogged batch        
   BatchStatement batchStatement = new BatchStatement(Type.UNLOGGED);        
   batchStatement.add(insertBind);        
   batchStatement.add(updateBind);        
   batchStatement.add(deleteBind);        
   session.execute(batchStatement);
   ```
   
 
