【问题标题】:Spring data Cassandra 2.0 Select BLOB column returns incorrect ByteBuffer dataSpring data Cassandra 2.0 Select BLOB column返回不正确的ByteBuffer数据
【发布时间】:2014-10-11 04:00:41
【问题描述】:

上下文:Spring data cassandra official 1.0.2.RELEASE from Maven Central repo, CQL3, cassandra 2.0, datastax driver 2.0.4

背景:cassandra blob 数据类型映射到 Java ByteBuffer。

下面的示例代码演示了在等效插入旁边使用 select 不会检索正确的字节。实际检索到的数据以大量垃圾字节为前缀,实际上看起来像是整行的序列化。 这个与 Cassandra 1.2 相关的older post 建议我们可能必须从长度为 ByteBuffer.remaining() 的 ByteBuffer.arrayOffset() 开始,但是 arrayOffset 的值实际上是 0。

我发现了一个 spring-data-cassandra 2.0.0。 SNAPSHOT 但 CassandraOperations API 有很大不同,它的包名也是:org.springdata... 与 org.springframework...

非常欢迎帮助解决此问题。

与此同时,我似乎必须对我的二进制数据与文本数据类型列进行 Base64 编码/解码。

--- 这里是我使用的简单表格 CQL 元数据-------------

CREATE TABLE person (
  id text,
  age int,
  name text,
  pict blob,
  PRIMARY KEY (id)
) ;

--- 遵循映射到 CQL 表的简单数据对象 ---

package org.spring.cassandra.example; 
 
import java.nio.ByteBuffer;
import org.springframework.data.cassandra.mapping.PrimaryKey; 
import org.springframework.data.cassandra.mapping.Table; 
 
@Table 
public class Person { 
 
 @PrimaryKey 
 private String id; 
 
 private int age; 
 private String name; 
 private ByteBuffer pict; 
 
 public Person(String id, int age, String name, ByteBuffer pict) { 
  this.id = id; this.name = name; this.age = age; this.pict = pict;
 } 
 
 public String getId() { return id; } 
 public String getName() { return name; } 
 public int getAge() { return age; } 
 public ByteBuffer getPict() { return pict; } 
     
 } 
 
}

--- 以及简单地插入和检索人员对象的纯 java 应用程序代码 --

package org.spring.cassandra.example;

import java.nio.ByteBuffer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.cassandra.core.CassandraOperations;

import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;

public class CassandraApp {

    private static final Logger logger = LoggerFactory
            .getLogger(CassandraApp.class);

    public static String hexDump(ByteBuffer bb) {
        char[] hexArray = "0123456789ABCDEF".toCharArray();
        bb.rewind();
        char[] hexChars = new char[bb.limit() * 2];
        for ( int j = 0; j < bb.limit(); j++ ) {
            int v = bb.get() & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        bb.rewind();
        return new String(hexChars);
    }

    public static void main(String[] args) {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app-context.xml");

        try {

            CassandraOperations cassandraOps = applicationContext.getBean(
                    "cassandraTemplate", CassandraOperations.class);

            cassandraOps.truncate("person");
            // prepare data
            byte[] ba = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x11, 0x22, 0x33, 0x44, 0x55, (byte) 0xAA, (byte) 0xCC, (byte) 0xFF };
            ByteBuffer myPict = ByteBuffer.wrap(ba);
            String myId = "1234567890";
            String myName = "mickey";
            int myAge = 50;
            
            logger.info("We try id=" + myId + ", name=" + myName + ", age=" + myAge +", pict=" + hexDump(myPict));
            
            cassandraOps.insert(new Person(myId, myAge, myName, myPict ));

            Select s = QueryBuilder.select("id","name","age","pict").from("person");
            s.where(QueryBuilder.eq("id", myId));

            ResultSet rs = cassandraOps.query(s);
            Row r = rs.one();
            
            logger.info("We got id=" + r.getString(0) + ", name=" + r.getString(1) + ", age=" + r.getInt(2) +", pict=" + hexDump(r.getBytes(3)));

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

--- 假设你已经为 cassandra 配置了一个简单的 Spring 项目 如http://projects.spring.io/spring-data-cassandra/

所述

实际执行产生:

[main] INFO org.spring.cassandra.example.CassandraApp - 我们尝试 id=1234567890, name=mickey, age=50, pict= 0001020304051122334455AACCFF

[主要] INFO org.spring.cassandra.example.CassandraApp - 我们得到了ID = 1234567890,姓名=米奇,年龄= 50,PICT = 8200000800000073000000020000000100000004000A6D796B657973706163650006706572736F6E00026964000D00046E616D65000D000361676500090004706963740003000000010000000A31323334353637383930000000066D69636B657900000004000000320000000E的 0001020304051122334455AACCFF 强>

虽然插入在数据库本身中看起来是正确的,但从 cqlsh 命令行中可以看出:

cqlsh:mykeyspace> select * from person;

 id         | age | name   | pict
------------+-----+--------+--------------------------------
 1234567890 |  50 | mickey | 0x0001020304051122334455aaccff

(1 rows)

【问题讨论】:

标签: cassandra blob bytebuffer spring-data-cassandra


【解决方案1】:

我遇到了完全相同的问题,但幸运的是找到了解决方案。 问题是 ByteBuffer 的使用令人困惑。尝试执行以下操作:

ByteBuffer bb = resultSet.one().getBytes("column_name");
byte[] data = new byte[bb.remaining()];
bb.get(data);

在此感谢 Sylvain 的建议:http://grokbase.com/t/cassandra/user/134brvqzd3/blobs-in-cql

【讨论】:

  • 当我们将范围缩小到字节缓冲区的“remaining()”部分时,确实有效。结论是带有 blob 的对象映射是错误的,您必须使用结果集作为您描述的替代方案,否则将您的对象映射限制为使用字符串并将 stringhex-or_B64 转换注入您的对象 getter/setter/constructors.. . 为什么我会期待下一个版本可以解决这个问题。
【解决方案2】:

查看 Datastax Java 驱动程序的 Bytes 类,它提供了对数据进行编码/解码所需的内容。

this post我写了一个用法示例。

HTH, 卡罗

【讨论】:

  • 谢谢,但是——如果我阅读并理解得很好——字节类只是促进从/到十六进制编码字符串的转换,本质上允许将 ByteBuffers 作为文本透明地存储在 cassandra 中,因此可以替换为我引用的 base64 编码将使二进制 blob 大小增加 33%,而使用十六进制编码则为 100%。本质上,这并没有解决根本问题,而是规避了它。
猜你喜欢
  • 1970-01-01
  • 2021-07-29
  • 2017-06-05
  • 1970-01-01
  • 2012-06-16
  • 2019-03-01
  • 1970-01-01
  • 2019-11-22
  • 1970-01-01
相关资源
最近更新 更多