我正在尝试实现一个Cassandra触发器,这样当keyspace1.tableA上有更新或删除时,触发器会向keyspace1.tableB添加一行。
tableB中列的名称与tableA中的列完全不同。
我正在使用Cassandra 2.1,没有选择转移到更新的版本。查看https://github.com/apache/cassandra/blob/cassandra-2.1/examples/triggers/src/org/apache/cassandra/triggers/InvertedIndex.java处的InvertedIndex触发器示例,我可以看到添加变异的基础知识:
来自InvertedIndex示例:
for (Cell cell : update)
{
// Skip the row marker and other empty values, since they lead to an empty key.
if (cell.value().remaining() > 0)
{
Mutation mutation = new Mutation(properties.getProperty("keyspace"), cell.value());
mutation.add(properties.getProperty("columnfamily"), cell.name(), key, System.currentTimeMillis());
mutations.add(mutation);
}
}
挑战在于,在此示例中,传递给mutation.add的单元名称是cell.name(),它是一个现有对象,我们可以使用该函数获取其名称。
目前,我只是想存储对tableA进行更改的时间,因此tableB有两列:
我需要添加一个突变,它将向tableB添加一行,并执行更改时间和操作。如何在Cassandra 2.1.12中添加这样的行变异?
我试过这个但是我在触发器中得到一个空指针异常:
...
String keycol = "changetime";
ByteBuffer uuidKey = ByteBuffer.wrap(UUIDGen.getTimeUUIDBytes());
ColumnIdentifier ci = new ColumnIdentifier(keycol, false);
CellName cn = CellNames.simpleSparse(ci);
mutation = new Mutation(keyspace, uuidKey);
mutation.add(tableName,cn, uuidKey, System.currentTimeMillis());
...
非常感谢任何帮助 - 我不了解Cassandra的内部结构,因此没有太多细节信息。
答案 0 :(得分:1)
答案是使用CFMetaData比较器来创建Mutation.add(...)所需的CellName。为了提供一个具体的例子,我将使用https://github.com/apache/cassandra/tree/cassandra-3.0/examples/triggers提供的Cassandra 3.0 AuditTrigger中的模式和示例
在这种情况下,我们要写的表是test.audit表,定义如下:
CREATE TABLE test.audit (key timeuuid, keyspace_name text,
table_name text, primary_key text, PRIMARY KEY(key));
此表有一个名为" key"的分区键。没有聚类列。有关定义,请参阅https://cassandra.apache.org/doc/cql3/CQL.html#createTableStmt,部分"分区键和聚类列。
这一点很重要,因为对makeCellName的调用(我们将在后面的示例代码中看到)采用变量参数列表,其中每个参数是我们希望相应的聚类列为该行采用的值。将受影响,最后一个参数是文本格式的列名称。
当没有聚类列时(如此架构中的情况),对makeCellName的调用只接受一个参数:列的名称。
将所有这些放在一起,Cassandra 2.1的AuditTrigger函数与3.0示例完全相同,如下所示:
public Collection<Mutation> augment(ByteBuffer key, ColumnFamily update)
{
CFMetaData cfm = update.metadata();
List<Mutation> mutations = new ArrayList<>(update.getColumnCount());
String keyspaceName = "";
String tableName = "";
String keyStr = "";
keyspaceName = cfm.ksName;
tableName = cfm.cfName;
try {
keyStr = ByteBufferUtil.string(key);
} catch (CharacterCodingException e) {
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
logger.error(errors.toString());
}
for (Cell cell : update)
{
// Skip the row marker and other empty values, since they lead to an empty key.
if (cell.value().remaining() > 0)
{
CFMetaData other = Schema.instance.getCFMetaData("test","audit");
CellNameType cnt = other.comparator;
ByteBuffer auditkey = UUIDType.instance.decompose(UUIDGen.getTimeUUID());
// create CellName objects for each of the columns in the audit table row we are inserting
CellName primaryKeyCellName = cnt.makeCellName("primary_key");
CellName keyspaceCellName = cnt.makeCellName("keyspace_name");
CellName tableCellName = cnt.makeCellName("table_name");
try {
// put the values we want to write to the audit table into ByteBuffer objects
ByteBuffer ksvalbb,tablevalbb,keyvalbb;
ksvalbb=ByteBuffer.wrap(keyspaceName.getBytes("UTF8"));
tablevalbb=ByteBuffer.wrap(tableName.getBytes("UTF8"));
keyvalbb=ByteBuffer.wrap(keyStr.getBytes("UTF8"));
// create the mutation object
Mutation mutation = new Mutation(keyspaceName, auditkey);
// get the time which will be needed for the call to mutation.add
long mutationTime=System.currentTimeMillis();
// add each of the column values to the mutation
mutation.add("audit", primaryKeyCellName, keyvalbb, mutationTime);
mutation.add("audit", keyspaceCellName, ksvalbb, mutationTime);
mutation.add("audit", tableCellName, tablevalbb, mutationTime);
mutations.add(mutation);
} catch (UnsupportedEncodingException e) {
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
logger.error(errors.toString());
}
}
}
return mutations;
}