Add tests for schemaExists and truncateSchema methods
Project: http://git-wip-us.apache.org/repos/asf/gora/repo Commit: http://git-wip-us.apache.org/repos/asf/gora/commit/89109b85 Tree: http://git-wip-us.apache.org/repos/asf/gora/tree/89109b85 Diff: http://git-wip-us.apache.org/repos/asf/gora/diff/89109b85 Branch: refs/heads/master Commit: 89109b855537050f5b9d125a88d26ca2c1d12f78 Parents: 2695794 Author: madhawa <madhaw...@gmail.com> Authored: Sun Jun 25 02:35:02 2017 +0530 Committer: madhawa <madhaw...@gmail.com> Committed: Sun Jun 25 02:41:37 2017 +0530 ---------------------------------------------------------------------- .../cassandra/store/CassandraQueryFactory.java | 146 ++++++++++++++++++- .../gora/cassandra/store/CassandraStore.java | 31 ++-- .../compositeKey/gora-cassandra-mapping.xml | 78 ++++++++++ ...stCassandraStoreWithNativeSerialization.java | 34 ++++- 4 files changed, 272 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/gora/blob/89109b85/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraQueryFactory.java ---------------------------------------------------------------------- diff --git a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraQueryFactory.java b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraQueryFactory.java index 422fbc6..fc90c5f 100644 --- a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraQueryFactory.java +++ b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraQueryFactory.java @@ -16,9 +16,13 @@ */ package org.apache.gora.cassandra.store; +import org.apache.gora.cassandra.bean.CassandraKey; +import org.apache.gora.cassandra.bean.ClusterKeyField; import org.apache.gora.cassandra.bean.Field; import org.apache.gora.cassandra.bean.KeySpace; +import org.apache.gora.cassandra.bean.PartitionKeyField; +import java.util.List; import java.util.Map; /** @@ -26,6 +30,13 @@ import java.util.Map; */ class CassandraQueryFactory { + /** + * This method returns the CQL query to create key space. + * refer : http://docs.datastax.com/en/cql/3.1/cql/cql_reference/create_keyspace_r.html + * + * @param mapping Cassandra Mapping + * @return CQL Query + */ static String getCreateKeySpaceQuery(CassandraMapping mapping) { KeySpace keySpace = mapping.getKeySpace(); StringBuilder stringBuffer = new StringBuilder(); @@ -59,6 +70,25 @@ class CassandraQueryFactory { StringBuilder stringBuffer = new StringBuilder(); stringBuffer.append("CREATE TABLE IF NOT EXISTS ").append(mapping.getKeySpace().getName()).append(".").append(mapping.getCoreName()).append(" ("); boolean isCommaNeeded = false; + CassandraKey cassandraKey = mapping.getCassandraKey(); + // appending Cassandra key columns into db schema + if (cassandraKey != null) { + for (PartitionKeyField partitionKeyField : cassandraKey.getPartitionKeyFields()) { + if (partitionKeyField.isComposite()) { + for (Field compositeField : partitionKeyField.getFields()) { + stringBuffer = processFields(stringBuffer, compositeField, isCommaNeeded); + } + + } else { + stringBuffer = processFields(stringBuffer, partitionKeyField, isCommaNeeded); + } + isCommaNeeded = true; + } + for (ClusterKeyField clusterKeyField : cassandraKey.getClusterKeyFields()) { + stringBuffer = processFields(stringBuffer, clusterKeyField, isCommaNeeded); + } + } + // appending Other columns for (Field field : mapping.getFieldList()) { if (isCommaNeeded) { stringBuffer.append(", "); @@ -69,22 +99,126 @@ class CassandraQueryFactory { if (isStaticColumn) { stringBuffer.append(" STATIC"); } - if(isPrimaryKey) { + if (isPrimaryKey) { stringBuffer.append(" PRIMARY KEY "); } isCommaNeeded = true; } + if (cassandraKey != null) { + List<PartitionKeyField> pkey = cassandraKey.getPartitionKeyFields(); + if (pkey != null) { + stringBuffer.append(", PRIMARY KEY ("); + boolean isCommaNeededToApply = false; + for (PartitionKeyField keyField : pkey) { + if (isCommaNeededToApply) { + stringBuffer.append(","); + } + if (keyField.isComposite()) { + stringBuffer.append("("); + boolean isCommaNeededHere = false; + for (Field field : keyField.getFields()) { + if (isCommaNeededHere) { + stringBuffer.append(", "); + } + stringBuffer.append(field.getColumnName()); + isCommaNeededHere = true; + } + stringBuffer.append(")"); + } else { + stringBuffer.append(keyField.getColumnName()); + } + isCommaNeededToApply = true; + } + stringBuffer.append(")"); + } + } + stringBuffer.append(")"); - if(Boolean.parseBoolean(mapping.getProperty("compactStorage"))) { + boolean isWithNeeded = true; + if (Boolean.parseBoolean(mapping.getProperty("compactStorage"))) { stringBuffer.append(" WITH COMPACT STORAGE "); - } else { - String id = mapping.getProperty("id"); - if (id != null) { - stringBuffer.append(" WITH ID = '").append(id).append("'"); + isWithNeeded = false; + } + + String id = mapping.getProperty("id"); + if (id != null) { + if (isWithNeeded) { + stringBuffer.append(" WITH "); + } else { + stringBuffer.append(" AND "); + } + stringBuffer.append("ID = '").append(id).append("'"); + isWithNeeded = false; + } + if (cassandraKey != null) { + List<ClusterKeyField> clusterKeyFields = cassandraKey.getClusterKeyFields(); + if (clusterKeyFields != null) { + if (isWithNeeded) { + stringBuffer.append(" WITH "); + } else { + stringBuffer.append(" AND "); + } + stringBuffer.append(" CLUSTERING ORDER BY ("); + boolean isCommaNeededToApply = false; + for (ClusterKeyField keyField : clusterKeyFields) { + if (isCommaNeededToApply) { + stringBuffer.append(", "); + } + stringBuffer.append(keyField.getColumnName()).append(" "); + if (keyField.getOrder() != null) { + stringBuffer.append(keyField.getOrder()); + } + isCommaNeededToApply = true; + } + stringBuffer.append(")"); } } return stringBuffer.toString(); } + private static StringBuilder processFields(StringBuilder stringBuilder, Field field, boolean isCommaNeeded) { + if (isCommaNeeded) { + stringBuilder.append(", "); + } + stringBuilder.append(field.getColumnName()).append(" ").append(field.getType()); + boolean isStaticColumn = Boolean.parseBoolean(field.getProperty("static")); + if (isStaticColumn) { + stringBuilder.append(" STATIC"); + } + return stringBuilder; + } + + /** + * This method returns the CQL query to drop table. + * refer : http://docs.datastax.com/en/cql/3.1/cql/cql_reference/drop_table_r.html + * + * @param mapping Cassandra Mapping + * @return CQL query + */ + static String getDropTableQuery(CassandraMapping mapping) { + return "DROP TABLE IF EXISTS " + mapping.getKeySpace().getName() + "." + mapping.getCoreName(); + } + + /** + * This method returns the CQL query to drop key space. + * refer : http://docs.datastax.com/en/cql/3.1/cql/cql_reference/drop_keyspace_r.html + * + * @param mapping Cassandra Mapping + * @return CQL query + */ + static String getDropKeySpaceQuery(CassandraMapping mapping) { + return "DROP KEYSPACE IF EXISTS " + mapping.getKeySpace().getName(); + } + + /** + * This method returns the CQL query to truncate (removes all the data) in the table. + * refer : http://docs.datastax.com/en/cql/3.1/cql/cql_reference/truncate_r.html + * + * @param mapping Cassandra Mapping + * @return CQL query + */ + static String getTruncateTableQuery(CassandraMapping mapping) { + return "TRUNCATE TABLE " + mapping.getKeySpace().getName() + "." + mapping.getCoreName(); + } } http://git-wip-us.apache.org/repos/asf/gora/blob/89109b85/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraStore.java ---------------------------------------------------------------------- diff --git a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraStore.java b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraStore.java index fad0fd4..113d0c4 100644 --- a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraStore.java +++ b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraStore.java @@ -20,12 +20,14 @@ package org.apache.gora.cassandra.store; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.HostDistance; +import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.PoolingOptions; import com.datastax.driver.core.ProtocolOptions; import com.datastax.driver.core.ProtocolVersion; import com.datastax.driver.core.QueryOptions; import com.datastax.driver.core.Session; import com.datastax.driver.core.SocketOptions; +import com.datastax.driver.core.TableMetadata; import com.datastax.driver.core.TypeCodec; import com.datastax.driver.core.policies.ConstantReconnectionPolicy; import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy; @@ -249,7 +251,7 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> Element partitionKeys = key.getChild("partitionKey"); Element clusterKeys = key.getChild("clusterKey"); List<Element> partitionKeyFields = partitionKeys.getChildren("field"); - List<Element> partitionCompositeKeyFields = partitionKeys.getChildren("compositeField"); + List<Element> partitionCompositeKeyFields = partitionKeys.getChildren("compositeKey"); // process non composite partition keys for (Element partitionKeyField : partitionKeyFields) { PartitionKeyField fieldKey = new PartitionKeyField(); @@ -458,7 +460,7 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> break; } default: - LOG.error("Unsupported Cassandra load balancing " + "policy: " + loadBalancingProp); + LOG.error("Unsupported Cassandra load balancing policy: {} ", loadBalancingProp); break; } } @@ -549,7 +551,7 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> break; } default: - LOG.error("Unsupported reconnection policy " + reconnectionPolicy); + LOG.error("Unsupported reconnection policy : {} ", reconnectionPolicy); } } return builder; @@ -578,7 +580,7 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> builder = builder.withRetryPolicy(new LoggingRetryPolicy(FallthroughRetryPolicy.INSTANCE)); break; default: - LOG.error("Unsupported retry policy " + retryPolicy); + LOG.error("Unsupported retry policy : {} ", retryPolicy); break; } } @@ -643,20 +645,23 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> @Override public String getSchemaName() { - return null; + return mapping.getCoreName(); } @Override public void createSchema() { - LOG.debug("creating Cassandra keyspace"); + LOG.debug("creating Cassandra keyspace {}", mapping.getKeySpace().getName()); this.session.execute(CassandraQueryFactory.getCreateKeySpaceQuery(mapping)); - LOG.debug("creating Cassandra column family / table"); + LOG.debug("creating Cassandra column family / table {}", mapping.getCoreName()); this.session.execute(CassandraQueryFactory.getCreateTableQuery(mapping)); } @Override public void deleteSchema() { - + LOG.debug("dropping Cassandra table {}", mapping.getCoreName()); + this.session.execute(CassandraQueryFactory.getDropTableQuery(mapping)); + LOG.debug("dropping Cassandra keyspace {}", mapping.getKeySpace().getName()); + this.session.execute(CassandraQueryFactory.getDropKeySpaceQuery(mapping)); } @Override @@ -765,11 +770,19 @@ public class CassandraStore<K, T extends Persistent> implements DataStore<K, T> @Override public void truncateSchema() { + LOG.debug("truncating Cassandra table {}", mapping.getCoreName()); + this.session.execute(CassandraQueryFactory.getTruncateTableQuery(mapping)); } @Override public boolean schemaExists() { - return false; + KeyspaceMetadata keyspace = cluster.getMetadata().getKeyspace(mapping.getKeySpace().getName()); + if (keyspace != null) { + TableMetadata table = keyspace.getTable(mapping.getCoreName()); + return table != null; + } else { + return false; + } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/89109b85/gora-cassandra-cql/src/test/conf/compositeKey/gora-cassandra-mapping.xml ---------------------------------------------------------------------- diff --git a/gora-cassandra-cql/src/test/conf/compositeKey/gora-cassandra-mapping.xml b/gora-cassandra-cql/src/test/conf/compositeKey/gora-cassandra-mapping.xml new file mode 100644 index 0000000..556d553 --- /dev/null +++ b/gora-cassandra-cql/src/test/conf/compositeKey/gora-cassandra-mapping.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one or more + ~ contributor license agreements. See the NOTICE file distributed with + ~ this work for additional information regarding copyright ownership. + ~ The ASF licenses this file to You under the Apache License, Version 2.0 + ~ (the "License"); you may not use this file except in compliance with + ~ the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- + The value of 'host' attribute of keyspace tag should match exactly what is in + gora.properties file. Essentially this means that if you are using port number, you should + use it every where regardless of whether it is the default port or not. + At runtime Gora will otherwise try to connect to localhost + https://issues.apache.org/jira/browse/GORA-269 + + The values of 'replication_factor' and 'placement_strategy' attribute of keyspace tag + only apply if gora create the kyespace. they have no effect if this is being used against + an existing keyspace. the default value for 'replication_factor' is '1' + + The value of 'placement_strategy' should be a fully qualifed class name that is known to + the cassansra cluster, not the application or gora. As of this writing, the classes that ship + with cassandra are: + 'org.apache.cassandra.locator.SimpleStrategy' + 'org.apache.cassandra.locator.NetworkTopologyStrategy' + gora cassandra would use SimpleStrategy by default if no value for this attribute is specified + + The default value of 'gc_grace_seconds' is '0' which is ONLY VIABLE FOR SINGLE NODE + CLUSTER. you should update this value according to your cluster configuration. + https://wiki.apache.org/cassandra/StorageConfiguration + + The value of 'ttl' (time to live) attribute of field tag should most likely always + be zero unless you want Cassandra to create Tombstones and delete portions of your + data once this period expires. Any positive value is read and bound to the number + of seconds after which the value for that field will disappear. The default value of ttl + is '0' + + More information on gora-cassandra configuration and mapping's can be found + at http://gora.apache.org/current/gora-cassandra.html +--> + + +<gora-otd> + <keyspace name="EmployeeSpace" durableWrite="false"> + <placementStrategy name="SimpleStrategy" replication_factor="1"/> + </keyspace> + + <class name="org.apache.gora.examples.generated.Employee" keyClass="org.apache.gora.examples.generated.WebPage" keyspace="EmployeeSpace" + table="Employee" compactStorage="true" id="31323131"> + <field name="name" column="name" type="text" ttl="10"/> + <field name="dateOfBirth" column="dob" type="timestamp" ttl="10"/> + </class> + + <cassandraKey name="org.apache.gora.examples.generated.WebPage"> + <partitionKey> + <compositeKey> + <field name="city" column="city" type="text"/> + <field name="country" column="country" type="text"/> + </compositeKey> + <field name="employerId" column="empID" type="int"/> + <field name="departmentId" column="deptID" type="int"/> + </partitionKey> + <clusterKey> + <field name="joinDate" column="joinDate" type="timestamp" order="desc"/> + </clusterKey> + </cassandraKey> + +</gora-otd> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/gora/blob/89109b85/gora-cassandra-cql/src/test/java/org/apache/gora/cassandra/store/TestCassandraStoreWithNativeSerialization.java ---------------------------------------------------------------------- diff --git a/gora-cassandra-cql/src/test/java/org/apache/gora/cassandra/store/TestCassandraStoreWithNativeSerialization.java b/gora-cassandra-cql/src/test/java/org/apache/gora/cassandra/store/TestCassandraStoreWithNativeSerialization.java index 217dea3..5bb1114 100644 --- a/gora-cassandra-cql/src/test/java/org/apache/gora/cassandra/store/TestCassandraStoreWithNativeSerialization.java +++ b/gora-cassandra-cql/src/test/java/org/apache/gora/cassandra/store/TestCassandraStoreWithNativeSerialization.java @@ -26,7 +26,7 @@ public class TestCassandraStoreWithNativeSerialization { public static void setUpClass() throws Exception { setProperties(); testDriver.setParameters(parameter); -// testDriver.setUpClass(); + testDriver.setUpClass(); userDataStore = testDriver.createDataStore(UUID.class, User.class); } @@ -47,7 +47,7 @@ public class TestCassandraStoreWithNativeSerialization { @AfterClass public static void tearDownClass() throws Exception { -// testDriver.tearDownClass(); + testDriver.tearDownClass(); } /** @@ -84,4 +84,34 @@ public class TestCassandraStoreWithNativeSerialization { User deletedUser = userDataStore.get(id); Assert.assertNull(deletedUser); } + + /** + * In this test case, schema exists method behavior of the data store is testing. + */ + @Test + public void testSchemaExists() { + userDataStore.deleteSchema(); + Assert.assertFalse(userDataStore.schemaExists()); + userDataStore.createSchema(); + Assert.assertTrue(userDataStore.schemaExists()); + } + + /** + * In this test case, schema exists method behavior of the data store is testing. + */ + @Test + public void testTruncateSchema() { + if(!userDataStore.schemaExists()) { + userDataStore.createSchema(); + } + UUID id = UUID.randomUUID(); + User user1 = new User(id, "Madhawa Kasun", Date.from(Instant.now())); + userDataStore.put(id, user1); + User olduser = userDataStore.get(id); + Assert.assertEquals(olduser.getName(), user1.getName()); + Assert.assertEquals(olduser.getDateOfBirth(), user1.getDateOfBirth()); + userDataStore.truncateSchema(); + olduser = userDataStore.get(id); + Assert.assertNull(olduser); + } }