datastax.repo_使用Datastax Java驱动程序与Cassandra进行交互
datastax.repo
今天,我這次返回了更多的Cassandra和Java集成,重點是使用Datastax Java驅動程序,而不是我已經寫了很多文章的Spring Data Cassandra。 Spring Data實際上使用了Datastax驅動程序來與Cassandra進行交互,但是在它之上還附帶了一些額外的功能。 但是我們今天不想要任何這些! 我們將直接使用Datastax驅動程序,并且在發布結束時,一旦看到如何使用它,我們便將其與Spring Data進行比較。
這篇文章假設您已經熟悉Cassandra,可能已經熟悉Spring Data Cassandra。 由于我已經寫了很多關于該主題的文章,所以我只討論了Cassandra在需要上下文的地方如何工作。 如果您沒有此背景信息,我建議您閱讀Spring Data Cassandra入門,在該文章中我顯然談到了使用Spring Data Cassandra的問題,而且比我在這篇文章中對Cassandra的工作方式進行了更詳盡的解釋。 還有Datastax學院 ,它提供了一些非常有用的資源來學習如何自己使用Cassandra。
首先,依賴性。
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>com.datastax.cassandra</groupId><artifactId>cassandra-driver-core</artifactId><version>3.4.0</version></dependency><dependency><groupId>com.datastax.cassandra</groupId><artifactId>cassandra-driver-mapping</artifactId><version>3.4.0</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.7</version></dependency> </dependencies>和往常一樣,我使用Spring Boot只是因為我們剝奪了自己的Spring Data并不意味著我們需要從所有Spring庫中完全擺脫掉。 這里與Datastax相關的依賴關系是cassandra-driver-core和cassandra-driver-mapping 。 顧名思義, cassandra-driver-core提供了與Cassandra交互的核心功能,例如建立會話和編寫查詢。 cassandra-driver-mapping不是查詢Cassandra所必需的,但確實提供了一些對象映射,它與核心驅動程序一起將用作ORM,而不僅僅是允許我們執行CQL語句。
現在,我們已經對依賴項進行了排序,下一步是連接到Cassandra,以便我們實際上可以開始查詢它了。
@Configuration public class CassandraConfig {@Beanpublic Cluster cluster(@Value("${cassandra.host:127.0.0.1}") String host,@Value("${cassandra.cluster.name:cluster}") String clusterName,@Value("${cassandra.port:9042}") int port) {return Cluster.builder().addContactPoint(host).withPort(port).withClusterName(clusterName).build();}@Beanpublic Session session(Cluster cluster, @Value("${cassandra.keyspace}") String keyspace)throws IOException {final Session session = cluster.connect();setupKeyspace(session, keyspace);return session;}private void setupKeyspace(Session session, String keyspace) throws IOException {final Map<String, Object> replication = new HashMap<>();replication.put("class", "SimpleStrategy");replication.put("replication_factor", 1);session.execute(createKeyspace(keyspace).ifNotExists().with().replication(replication));session.execute("USE " + keyspace);// String[] statements = split(IOUtils.toString(getClass().getResourceAsStream("/cql/setup.cql")), ";");// Arrays.stream(statements).map(statement -> normalizeSpace(statement) + ";").forEach(session::execute);}@Beanpublic MappingManager mappingManager(Session session) {final PropertyMapper propertyMapper =new DefaultPropertyMapper().setNamingStrategy(new DefaultNamingStrategy(LOWER_CAMEL_CASE, LOWER_SNAKE_CASE));final MappingConfiguration configuration =MappingConfiguration.builder().withPropertyMapper(propertyMapper).build();return new MappingManager(session, configuration);} }與使用Spring Data的類似設置相比,這里有更多的核心(與Spring Boot的自動配置結合使用時,甚至不需要該類),但是該類本身非常簡單。 此處顯示的Cluster和Session Bean的基本設置是應用程序正常工作所需的最低要求,并且對于您編寫的任何應用程序都可能保持不變。 提供了更多方法,因此您可以添加任何其他配置以使其適合您的用例。
通過使用來自值application.properties我們設置的主機地址,集群名和端口Cluster 。 然后,將Cluster用于創建Session 。 執行此操作時,有兩個選項可供選擇,是否設置默認鍵空間。 如果要設置默認鍵空間,則只需使用下面的代碼即可。
@Bean public Session session(Cluster cluster, @Value("${cassandra.keyspace}") String keyspace) throws IOException {final Session session = cluster.connect(keyspace);// any other setupreturn session; }密鑰空間被傳遞到connect方法中,該方法將創建一個Session ,然后執行USE <keyspace>從而設置默認的密鑰空間。 這依賴于創建會話之前存在的鍵空間,如果不存在,則在執行USE語句時將失敗。
如果您不知道啟動時是否存在鍵空間,或者您肯定要基于屬性文件中的鍵空間值動態創建鍵空間,則需要調用connect而不指定鍵空間。 然后,您將需要自己創建它,以便實際使用。 為此,請使用SchemaBuilder提供的createKeyspace方法。 以下是用于創建密鑰空間的CQL語句。
CREATE KEYSPACE IF NOT EXISTS <keyspace> WITH REPLICATION = { 'class':'SimpleStrategy', 'replication_factor':1 };我還再次在下面添加了鍵空間代碼,因為它離現在有點遠。
private void setupKeyspace(Session session, String keyspace) throws IOException {final Map<String, Object> replication = new HashMap<>();replication.put("class", "SimpleStrategy");replication.put("replication_factor", 1);session.execute(createKeyspace(keyspace).ifNotExists().with().replication(replication));session.execute("USE " + keyspace); }SchemaBuilder非常易于使用,并且在您瀏覽CQL時看起來非常相似。 我們添加一個ifNotExists條款和先調用設置復制的因素with ,然后通過一個Map<String, Object>進入replicationMethod 。 該映射需要包含類和復制因子,基本上使用此處顯示的鍵,但是將映射的值更改為您需要的值。 不要忘記execute該語句,然后告訴會話使用剛創建的鍵空間。 不幸的是,沒有更好的方法手動設置默認鍵空間,唯一的選擇是執行USE語句。
接下來是關于設置默認鍵空間的前兩個選項。 如果我們選擇完全不設置默認鍵空間,則需要在創建的每個表和執行的每個查詢之前添加鍵空間。 Datastax提供了向查詢以及映射實體添加鍵空間名稱的方法,這并不難。 我不會再進一步??討論這個主題,但是要知道,如果正確設置了其他所有內容,則不設置鍵空間不會阻止您的應用程序正常工作。
設置鍵空間后,我們便可以創建表。 有兩種方法可以做到這一點。 一種是執行一些CQL語句,無論它們是Java代碼中的字符串還是從外部CQL腳本中讀取的字符串。 二,使用SchemaBuilder創建它們。
讓我們看一下首先執行CQL語句,或更準確地說是從CQL文件執行它們。 您可能已經注意到,在原始示例中我留下了一些注釋掉的代碼,如果沒有注釋,該代碼將找到一個名為setup.cql的文件,讀出一個CQL語句,執行它,然后移至下一條語句。 又來了。
String[] statements = split(IOUtils.toString(getClass().getResourceAsStream("/cql/setup.cql")), ";"); Arrays.stream(statements).map(statement -> normalizeSpace(statement) + ";").forEach(session::execute);以下是創建Cassandra表的文件中包含的CQL。
REATE TABLE IF NOT EXISTS people_by_country(country TEXT,first_name TEXT,last_name TEXT,id UUID,age INT,profession TEXT,salary INT,PRIMARY KEY((country), first_name, last_name, id) );主鍵由country , first_name , last_name和id字段組成。 分區鍵僅由country字段組成,聚類列是鍵中的其余鍵,僅出于唯一性而包含id ,因為您顯然可以使人具有相同的名字。 我在之前的文章Spring Data Cassandra入門中更深入地討論了主鍵主題。
此代碼利用commons-io和commons-lang3依賴性。 如果我們不是以這種方式執行CQL,則可以刪除這些依賴關系(在本文的上下文中)。
關于使用SchemaBuilder呢? 我沒有在原始代碼段中包含任何用于創建表的代碼,因為我在玩耍并試圖找出放置它的最佳位置,目前我將其粘貼在存儲庫中,但我仍然不相信這就是完美的地方。 無論如何,我將代碼粘貼到此處,以便我們現在可以查看它,然后在它再次出現時可以跳過它。
private void createTable(Session session) {session.execute(SchemaBuilder.createTable(TABLE).ifNotExists().addPartitionKey("country", text()).addClusteringColumn("first_name", text()).addClusteringColumn("last_name", text()).addClusteringColumn("id", uuid()).addColumn("age", cint()).addColumn("profession", text()).addColumn("salary", cint())); }這與上面顯示的CQL非常匹配。 我們可以使用addPartitionKey和addClusteringColumn定義不同的列類型, addClusteringColumn標準字段創建主鍵和addColumn 。 還有許多其他方法,例如addStaticColumn和withOptions允許您隨后調用clusteringOrder來定義集群列的排序方向。 調用這些方法的順序非常重要,因為分區鍵和群集列將按照調用它們各自方法的順序來創建。 Datastax還提供了DataType類,以簡化列類型的定義,例如, text與TEXT匹配, cint與INT匹配。 與上一次使用SchemaBuilder ,一旦我們對表設計感到滿意,就需要execute它。
在MappingManager ,下面是創建Bean的代碼段。
@Bean public MappingManager mappingManager(Session session) {final PropertyMapper propertyMapper =new DefaultPropertyMapper().setNamingStrategy(new DefaultNamingStrategy(LOWER_CAMEL_CASE, LOWER_SNAKE_CASE));final MappingConfiguration configuration =MappingConfiguration.builder().withPropertyMapper(propertyMapper).build();return new MappingManager(session, configuration); }MappingManager bean來自cassandra-driver-mapping依賴項,它將ResultSet映射到一個實體(稍后我們將進行介紹)。 現在,我們只需要創建bean。 如果對Cassandra中沒有分隔符的Java駝峰大小寫轉換為所有小寫字母的默認命名策略不滿意,我們將需要設置自己的名字。 為此,我們可以傳入DefaultNamingStrategy來定義我們在Java類中使用的情況以及在Cassandra中使用的情況。 由于在Java中通常使用駝峰大小寫,因此我們傳入LOWER_CAMEL_CASE并且由于我喜歡在Cassandra中使用蛇形大小寫,因此我們可以使用LOWER_SNAKE_CASE (可以在NamingConventions類中找到)。 較低的引用指定字符串中第一個字符的大小寫,因此LOWER_CAMEL_CASE表示firstName而UPPER_CAMEL_CASE表示FirstName 。 DefaultPropertyMapper帶有用于更具體配置的額外方法,但是MappingConfiguration僅具有一項工作,即接收要傳遞給MappingManager的PropertyMapper 。
我們接下來要看的是將持久保留到Cassandra并從Cassandra中檢索到的實體,從而節省了我們手動設置插入值和轉換讀取結果的工作量。 Datastax驅動程序為我們提供了一種相對簡單的方法,通過使用批注來標記屬性(例如其要映射的表的名稱),哪個字段與Cassandra列匹配以及哪個字段由主鍵組成。
@Table(name = "people_by_country") public class Person {@PartitionKeyprivate String country;@ClusteringColumnprivate String firstName;@ClusteringColumn(1)private String lastName;@ClusteringColumn(2)private UUID id;private int age;private String profession;private int salary;private Person() {}public Person(String country, String firstName, String lastName, UUID id, int age, String profession, int salary) {this.country = country;this.firstName = firstName;this.lastName = lastName;this.id = id;this.age = age;this.profession = profession;this.salary = salary;}// getters and setters for each property// equals, hashCode, toString }該實體表示@Table表示的people_by_country表。 我再次將下表的CQL放置以供參考。
CREATE TABLE IF NOT EXISTS people_by_country(country TEXT,first_name TEXT,last_name TEXT,id UUID,age INT,profession TEXT,salary INT,PRIMARY KEY((country), first_name, last_name, id) );該@Table注釋必須指定實體代表表的名稱,還配備了根據您的要求,如各種其它選項keyspace ,如果你不想使用默認密鑰空間的Session bean被配置為使用和caseSensitiveTable這是不言自明的。
那主鍵呢? 如上文所述,主鍵由一個分區鍵組成,分區鍵本身包含一個或多個列和/或群集列。 為了與上面定義的Cassandra表匹配,我們在必填字段中添加了@PartitionKey和@ClusteringColumn批注。 這兩個注釋都具有一個屬性,即value ,它指定列在主鍵中的顯示順序。 默認值為0 ,這就是為什么某些注釋不包含值的原因。
使該實體正常工作的最后一個要求是getter,setter和默認構造函數,以便映射器能夠完成任務。 如果您不希望任何人訪問默認構造函數,則默認構造函數可以是私有的,因為映射器使用反射來檢索它。 您可能不想在實體上設置setter,因為您希望對象是不變的,但是不幸的是,您對此無能為力,而您只需要讓這場戰斗成為可能。 盡管我個人認為這很好,因為您可以(也許應該)將實體轉換為可以在應用程序中傳遞的另一個對象,而無需任何實體注釋,因此無需了解數據庫本身。 然后,該實體可以保持可變,而您傳遞的另一個對象可以完全按照您的期望工作。
在繼續之前,我想提的最后一件事。 還記得我們之前定義的DefaultNamingConvention嗎? 這意味著我們的字段將與正確的列匹配,而無需在實體中進行任何額外的工作。 如果您沒有執行此操作,或者想為列名提供不同的字段名,則可以使用@Column批注并在其中指定。
我們幾乎擁有構建示例應用程序所需的所有組件。 倒數第二個組件正在創建一個存儲庫,其中將包含用于持久存儲和從Cassandra讀取數據的所有邏輯。 我們將利用我們先前創建的MappingManager bean和我們放置在實體上的注釋將ResultSet轉換為實體,而無需自己做任何其他事情。
@Repository public class PersonRepository {private Mapper<Person> mapper;private Session session;private static final String TABLE = "people_by_country";public PersonRepository(MappingManager mappingManager) {createTable(mappingManager.getSession());this.mapper = mappingManager.mapper(Person.class);this.session = mappingManager.getSession();}private void createTable(Session session) {// use SchemaBuilder to create table}public Person find(String country, String firstName, String secondName, UUID id) {return mapper.get(country, firstName, secondName, id);}public List<Person> findAll() {final ResultSet result = session.execute(select().all().from(TABLE));return mapper.map(result).all();}public List<Person> findAllByCountry(String country) {final ResultSet result = session.execute(select().all().from(TABLE).where(eq("country", country)));return mapper.map(result).all();}public void delete(String country, String firstName, String secondName, UUID id) {mapper.delete(country, firstName, secondName, id);}public Person save(Person person) {mapper.save(person);return person;} }通過構造函數注入MappingManager并調用Person類的mapper方法,將返回一個Mapper<Person> ,它將親自處理我們所有的映射需求。 我們還需要檢索Session才能執行查詢,該查詢很好地包含在我們注入的MappingManager 。
對于三個查詢,我們直接依賴于映射器與Cassandra進行交互,但這僅適用于單個記錄。 通過接受組成Person實體的主鍵的值來get , save和delete每個作品,并且必須以正確的順序輸入它們,否則您將遇到意想不到的結果,否則將引發異常。
其他情況要求在調用映射器之前執行查詢,以將返回的ResultSet轉換為實體或實體集合。 我已經使用QueryBuilder編寫查詢,并且我也選擇了不編寫準備好的語句。 盡管在大多數情況下您應該使用準備好的語句,但我想我將來會在單獨的文章中介紹這些語句,盡管它們足夠相似,并且QueryBuilder仍然可以使用,所以我相信您可以根據需要自行解決。
QueryBuilder提供了靜態方法來創建select , insert , update和delete語句,然后可以將它們鏈接在一起以構建查詢(我知道這很明顯)。 當您需要手動創建自己的查詢而不依賴于來自Cassandra存儲庫的推斷查詢時,此處使用的QueryBuilder也可以在Spring Data Cassandra中使用。
創建這個小應用程序的最后一步實際上是在運行它。 由于我們使用的是Spring Boot,因此只需添加標準@SpringBootApplication并運行該類。 我已經在下面做了這些,以及使用CommandLineRunner在存儲庫中執行了這些方法,以便我們可以檢查它們是否在執行我們期望的工作。
@SpringBootApplication public class Application implements CommandLineRunner {@Autowiredprivate PersonRepository personRepository;public static void main(String args[]) {SpringApplication.run(Application.class);}@Overridepublic void run(String... args) {final Person bob = new Person("UK", "Bob", "Bobbington", UUID.randomUUID(), 50, "Software Developer", 50000);final Person john = new Person("UK", "John", "Doe", UUID.randomUUID(), 30, "Doctor", 100000);personRepository.save(bob);personRepository.save(john);System.out.println("Find all");personRepository.findAll().forEach(System.out::println);System.out.println("Find one record");System.out.println(personRepository.find(john.getCountry(), john.getFirstName(), john.getLastName(), john.getId()));System.out.println("Find all by country");personRepository.findAllByCountry("UK").forEach(System.out::println);john.setProfession("Unemployed");john.setSalary(0);personRepository.save(john);System.out.println("Demonstrating updating a record");System.out.println(personRepository.find(john.getCountry(), john.getFirstName(), john.getLastName(), john.getId()));personRepository.delete(john.getCountry(), john.getFirstName(), john.getLastName(), john.getId());System.out.println("Demonstrating deleting a record");System.out.println(personRepository.find(john.getCountry(), john.getFirstName(), john.getLastName(), john.getId()));} }run方法包含一些打印行,因此我們可以看到發生了什么,下面是它們的輸出。
Find all Person{country='US', firstName='Alice', lastName='Cooper', id=e113b6c2-5041-4575-9b0b-a0726710e82d, age=45, profession='Engineer', salary=1000000} Person{country='UK', firstName='Bob', lastName='Bobbington', id=d6af6b9a-341c-4023-acb5-8c22e0174da7, age=50, profession='Software Developer', salary=50000} Person{country='UK', firstName='John', lastName='Doe', id=f7015e45-34d7-4f25-ab25-ca3727df7759, age=30, profession='Doctor', salary=100000}Find one record Person{country='UK', firstName='John', lastName='Doe', id=f7015e45-34d7-4f25-ab25-ca3727df7759, age=30, profession='Doctor', salary=100000}Find all by country Person{country='UK', firstName='Bob', lastName='Bobbington', id=d6af6b9a-341c-4023-acb5-8c22e0174da7, age=50, profession='Software Developer', salary=50000} Person{country='UK', firstName='John', lastName='Doe', id=f7015e45-34d7-4f25-ab25-ca3727df7759, age=30, profession='Doctor', salary=100000}Demonstrating updating a record Person{country='UK', firstName='John', lastName='Doe', id=f7015e45-34d7-4f25-ab25-ca3727df7759, age=30, profession='Unemployed', salary=0}Demonstrating deleting a record null我們可以看到findAll返回了所有記錄,而find僅檢索了與輸入主鍵值匹配的記錄。 findAllByCountry已排除了愛麗絲,僅從英國找到了記錄。 在現有記錄上再次調用save將更新記錄,而不是插入。 最后, delete將從數據庫中刪除該人的數據(例如刪除facebook?!?!)。
多數民眾贊成在一個包裝。
將來,我將嘗試編寫一些后續文章,因為我們可以使用本文中未涉及的Datastax驅動程序來做一些更有趣的事情。 我們在這里介紹的內容足以使您邁出使用驅動程序的第一步,并開始從您的應用程序查詢Cassandra。
在我們開始之前,我想對Datastax驅動程序和Spring Data Cassandra進行一些比較。
與Spring Data Cassandra相比,Datastax驅動程序缺乏對創建表的支持(我認為)。 Spring Data能夠僅基于您的實體創建表的事實消除了所有這些工作,基本上可以重寫您已經編寫的內容。 顯然,如果您不想使用實體注釋,那么區別就消失了,因為您將需要在Datastax和Spring Data中手動創建表。
實體的設計方式和使用的注釋也大不相同。 這一點與我先前提出的觀點緊密相關。 因為Spring Data可以為您創建表,所以它對更精確的批注的需求更大,這些批注允許您指定表的設計,例如集群列的排序順序。 顯然,這會使類變得雜亂無章,而通常不會出現這樣的注釋。
Spring Data還為標準查詢(如findAll和插入實體集合)提供了更好的支持。 顯然,這并不是世界末日,實現這些將花費很少的精力,但這幾乎總結了Datastax驅動程序和Spring Data Cassandra之間的主要區別。
Spring Data更加易于使用。 我認為關于這個話題真的沒有什么要說的。 由于Spring Data Cassandra是基于Datastax驅動程序構建的,因此它顯然可以執行驅動程序可以執行的所有操作,如果缺少所需的任何內容,則可以直接訪問Datastax類并執行所需的操作。 但是不應過分考慮Spring Data提供的便利,而且我認為我什至沒有覆蓋它提供的一些更有用的部分,因為本文僅涵蓋了基礎知識。 甚至不要讓我開始使用Spring Boot的自動配置和Cassandra存儲庫為您生成的推斷查詢后,它變得多么容易。
我應該停止……這變成了咆哮。
總之,使用Datastax驅動程序連接和查詢Cassandra數據庫是相對簡單的。 建立與Cassandra的連接,創建所需的實體,并編寫使用前者的存儲庫,然后便擁有了進行所需的一切。 我們還將Datastax驅動程序與Spring Data Cassandra進行了比較,這可以歸結為,Datastax可以滿足您的需求,但是Spring Data使其更容易。
這篇文章中使用的代碼可以在我的GitHub上找到 。
如果您發現此帖子有幫助,并希望了解我的最新帖子,那么可以通過@LankyDanDev在Twitter上關注我。
翻譯自: https://www.javacodegeeks.com/2018/04/interacting-with-cassandra-using-the-datastax-java-driver.html
datastax.repo
總結
以上是生活随笔為你收集整理的datastax.repo_使用Datastax Java驱动程序与Cassandra进行交互的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓画板app(安卓画板)
- 下一篇: 如何注销域名备案(如何注销域名备案号)