驅動下載
創建maven工程,讓maven來維護我們的jar,maven最重要的pom文件內容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.huawei</groupId> <artifactId>cassandra</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>cassandra</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.datastax.cassandra</groupId> <artifactId>cassandra-driver-core</artifactId> <version>2.1.10.3</version> </dependency> </dependencies> </project>
Session獲取
官方給的Quick start案例
Cluster cluster = null; try { cluster = Cluster.builder() // (1) .addContactPoint("127.0.0.1") // cassandra服務器ip .withCredentials("admin", "admin") // 若沒有啟用賬號認證,此處可以去掉 .build(); Session session = cluster.connect(); // (2) ResultSet rs = session.execute("select release_version from system.local"); // (3) Row row = rs.one(); System.out.println(row.getString("release_version")); // (4) } finally { if (cluster != null) cluster.close(); // (5) }
代碼中的(1) ~ (5)分別表示或者代表什么
(1):Cluster對象是驅動程序的主入口點,它保存着真實Cassandra集群的狀態(尤其是元數據);Cluster是線程安全的,一個Cassandra集群創建一個Cluster的單例,整個應用用這一個單例即可
(2):Session用來執行查詢的,而且它也是線程安全的,同樣也應該重復利用
(3):利用execute來發送一個查詢到Cassandra,execute返回一個Resultset(結果集),這個結果集就是必要的列的行集合(二維表,行是滿足條件的記錄,列是我們關注的某些字段)
(4):從row中提取數據
(5):當任務完成后,關閉cluster,關閉cluster的同時將會關閉它創建的全部session;這一步很重要,它會釋放潛在的資源(TCP連接、線程池等),在真實的應用中,我們應該在應用關閉(或應用卸載)的時候關閉cluster
有jdbc開發的經驗,就會發現,上述代碼似曾相識,上述代碼中的session就相當於jdbc中的connection,是整個數據庫操作的基礎,那么我們將session的獲取單獨抽出來
package com.huawei.cassandra.factory; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; public class SessionRepository { private static Session instance = null; private static Cluster cluster = null; private static Lock lock = new ReentrantLock(); private SessionRepository(){} public static Session getSession() { if (null == instance) { try { lock.lock(); if (null == instance) { cluster = Cluster.builder() .addContactPoint("127.0.0.1") .withCredentials("admin", "admin") .build(); instance = cluster.connect(); // 也可以針對一個特定的keyspace獲取一個session // instance = cluster.connect("mycas"); } } finally { lock.unlock(); } } return instance; } public static void close() { if (null == cluster) { try { lock.lock(); if (null == cluster) { cluster.close(); } } finally { lock.unlock(); } } } }
拿到session了,那么請隨意操作Cassandra吧!
cassandra基本操作
創建表
在mycas下創建表student
use mycas; create table student( id int, address text, name text, age int, height int, primary key(id,address,name) ); insert into student(id,address,name,age,height) values(1,'guangdong','lixiao',32,172);
session直接執行cql
和jdbc類似,關鍵是cql的拼接,下例是插入一條記錄,刪、改、查和這類似,不一一列舉了
// 字符串注意單引號' String cql = "insert into mycas.student(id,address,name,age,height) values(" + student.getId() + ",'" + student.getAddress() + "','" + student.getName() + "'," + student.getAge() + "," + student.getHeight() + ");"; System.out.println(cql); session.execute(cql);
Querybuilder
利用Querybuilder可以減輕cql的拼接,sql語句的拼接由驅動完成
查詢一個student:
@Override public Student getStudentByKeys(int id, String address, String name) { Student student = null; ResultSet rs = session.execute( QueryBuilder.select("id", "address", "name", "age", "height") .from("mycas", "student") .where(QueryBuilder.eq("id", id)) .and(QueryBuilder.eq("address", address)) .and(QueryBuilder.eq("name", name))); Iterator<Row> rsIterator = rs.iterator(); if (rsIterator.hasNext()) { Row row = rsIterator.next(); student = new Student(); student.setAddress(row.getString("address")); student.setAge(row.getInt("age")); student.setHeight(row.getInt("height")); student.setId(row.getInt("id")); student.setName(row.getString("name")); } return student; }
保存一個student:
@Override public void saveStudent(Student student) { session.execute( QueryBuilder.insertInto("mycas", "student") .values(new String[]{"id", "address", "name", "age", "height"}, new Object[]{student.getId(), student.getAddress(), student.getName(), student.getAge(), student.getHeight()})); }
修改一個student:
@Override public void updateStudent(Student student) { session.execute( QueryBuilder.update("mycas", "student") .with(QueryBuilder.set("age", student.getAge())) .and(QueryBuilder.set("height", student.getHeight())) .where(QueryBuilder.eq("id", student.getId())) .and(QueryBuilder.eq("address", student.getAddress())) .and(QueryBuilder.eq("name", student.getName()))); }
刪除一個student:
@Override public void removeStudent(int id, String address, String name) { session.execute(QueryBuilder.delete() .from("mycas", "student") .where(QueryBuilder.eq("id", id)) .and(QueryBuilder.eq("address", address)) .and(QueryBuilder.eq("name", name))); }
注意:驅動版本不同,Querybuilder的用法有些許不同,有些版本的某些方法變成非靜態的了!
占位符
cassandra也有類似jdbc那樣使用預編譯占位符
http://docs.datastax.com/en/developer/java-driver/3.0/manual/statements/prepared/
預編譯的原理是怎樣的了,上面的鏈接是驅動官方的解釋,我來談談我的理解
當我們預編譯statement的時候,Cassandra會解析query語句,緩存解析的結果並返回一個唯一的標志(PreparedStatement對象保持着這個標志的內部引用,就相當於通過標志可以獲取到query語句預編譯后的內容
):
當你綁定並且執行預編譯statement的時候,驅動只會發送這個標志,那么Cassandra就會跳過解析query語句的過程:
所以,我們應該保證query語句只應該被預編譯一次,緩存PreparedStatement
到我們的應用中(PreparedStatement
是線程安全的);如果我們對同一個query語句預編譯了多次,那么驅動會打印警告日志;如果一個query語句只執行一次,那么預編譯不會提供性能上的提高,反而會降低性能,因為它是兩個來回(結合上面兩張圖),那么此時可以考慮用 simple statement 來代替
和jdbc的預編譯非常類似,我們來看看實際代碼
靜態cql
private static final String GET_STUDENT = "select id,address,name,age,height from mycas.student where id=? and address=? and name=?;"; private static final String SAVE_STUDENT = "insert into mycas.student(id,address,name,age,height) values(?,?,?,?,?);"; private static final String UPDATE_STUDENT = "update mycas.student set age=?, height=? where id=? and address=? and name=?;"; private static final String REMOVE_STUDENT = "delete from mycas.student where id=? and address=? and name=?;";
查詢一個student
Student student = null; PreparedStatement prepareStatement = session.prepare(GET_STUDENT); BoundStatement bindStatement = new BoundStatement(prepareStatement).bind(id, address, name); ResultSet rs = session.execute(bindStatement); Iterator<Row> rsIterator = rs.iterator(); if (rsIterator.hasNext()) { Row row = rsIterator.next(); student = new Student(); student.setAddress(row.getString("address")); student.setAge(row.getInt("age")); student.setHeight(row.getInt("height")); student.setId(row.getInt("id")); student.setName(row.getString("name")); } return student;
保存一個student
PreparedStatement prepareStatement = session.prepare(SAVE_STUDENT); BoundStatement bindStatement = new BoundStatement(prepareStatement) .bind(student.getId(), student.getAddress(), student.getName(), student.getAge(), student.getHeight()); session.execute(bindStatement);
修改一個student
PreparedStatement prepareStatement = session.prepare(UPDATE_STUDENT); BoundStatement bindStatement = new BoundStatement(prepareStatement) .bind(student.getAge(), student.getHeight(), student.getId(), student.getAddress(), student.getName()); session.execute(bindStatement);
刪除一個student
PreparedStatement prepareStatement = session.prepare(REMOVE_STUDENT); BoundStatement bindStatement = new BoundStatement(prepareStatement) .bind(id, address, name); session.execute(bindStatement);
批量batch
public static void batch() { Session session = SessionRepository.getSession(); BoundStatement insertBind1 = new BoundStatement( session.prepare("insert into mycas.student(id,address,name,age,height) values(?,?,?,?,?);")) .bind(3, "guangxi", "huangfeihong", 67, 175); BoundStatement insertBind2 = new BoundStatement( session.prepare("insert into mycas.student(id,address,name,age,height) values(?,?,?,?,?);")) .bind(4, "hunan", "youzhibing", 26, 160); BoundStatement updateBind = new BoundStatement( session.prepare("update mycas.student set age=?, height=? where id=? and address=? and name=?;")) .bind(72, 173, 3, "guangxi", "huangfeihong"); BoundStatement deleteBind = new BoundStatement( session.prepare("delete from mycas.student where id=? and address=? and name=?;")) .bind(4, "hunan", "youzhibing"); BatchStatement batchStatement = new BatchStatement(); batchStatement.add(insertBind1); batchStatement.add(insertBind2); batchStatement.add(updateBind); batchStatement.add(deleteBind); session.execute(batchStatement); }
public static void batch() { Session session = SessionRepository.getSession(); BoundStatement insertBind1 = new BoundStatement( session.prepare("insert into mycas.student(id,address,name,age,height) values(?,?,?,?,?);")) .bind(3, "guangxi", "huangfeihong", 67, 175); BoundStatement insertBind2 = new BoundStatement( session.prepare("insert into mycas.student(id,address,name,age,height) values(?,?,?,?,?);")) .bind(4, "hunan", "youzhibing", 26, 160); BoundStatement updateBind = new BoundStatement( session.prepare("update mycas.student set age=?, height=? where id=? and address=? and name=?;")) .bind(72, 173, 3, "guangxi", "huangfeihong"); BoundStatement deleteBind = new BoundStatement( session.prepare("delete from mycas.student where id=? and address=? and name=?;")) .bind(4, "hunan", "youzhibing"); BatchStatement batchStatement = new BatchStatement(); batchStatement.add(insertBind1); batchStatement.add(insertBind2); batchStatement.add(updateBind); batchStatement.add(deleteBind); session.execute(batchStatement); }