zoukankan      html  css  js  c++  java
  • spring data 操作 Cassandra 踩坑二三事

    spring data 操作 Cassandra

    添加依赖和配置

    • 添加依赖
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-cassandra</artifactId>
            </dependency>
    
    • 添加配置
    spring.data.cassandra.keyspace-name=myKeySpace
    spring.data.cassandra.contact-points=myContactPoints
    spring.data.cassandra.port=9042
    spring.data.cassandra.schema-action=NONE
    

    定义key, data 和 建表

    此处以counter表为例, 表示用户在某一个类别下的点击量。

    @PrimaryKeyClass
    public class UserCategoryKey implements Serializable {
    
        @PrimaryKeyColumn(name = "user_id", ordinal = 0, type = PrimaryKeyType.PARTITIONED)
        private String userId;
    
        @PrimaryKeyColumn(name = "category_id", ordinal = 1, type = PrimaryKeyType.CLUSTERED)
        private Integer categoryId;
    }
    
    @Table("user_category")
    public class UserCategory {
        @PrimaryKey
        private UserCategoryKey key;
    
        @Column("click_cnt")
        private Long clickCnt;
    }
    

    创建表的schema:

    -- Cannot set default_time_to_live on a table with counters
    CREATE TABLE user_category
    (
        user_id text,
        category_id int,
        click_cnt counter
        PRIMARY KEY ((user_id), category_id)
    );
    

    限制:

    • 非counter字段必须包含在主键中。

    使用repository操作数据

    @Repository
    public interface UserCategoryRepository extends CassandraRepository<UserCategory, UserCategoryKey> {
    
        @Modifying
        @Transactional
        @Query(value = "update user_category set click_cnt = click_cnt + :cnt where " +
                "user_id = :#{#key.userId} and category_id = :#{#key.categoryId}")
        public void incClickCnt(@Param("key") UserCategoryKey key, @Param("cnt") Long cnt);
    }
    

    使用CassandraOperations 操作数据

    CassandraOperations 可以直接注入:

        @Autowired
        protected CassandraOperations cassandraTemplate;
        /**
         * update user_category set click_cnt = click_cnt + 1 where user_id='user1' and category_id = 2001
         */
        public Boolean incClickCounter(UserCategoryKey key, Long cnt) {
            try {
    	        //update table
                Update update = QueryBuilder.update("user_category");
                //set
                update.with(QueryBuilder.incr("click_cnt", cnt));
                //where conditions
                update.where(QueryBuilder.eq("user_id", key.getUserId()))
                        .and(QueryBuilder.eq("category_id", key.getCategoryId()));
                cassandraTemplate.getCqlOperations().execute(update);
                return true;
            } catch (Exception e) {
                LOG.error("incClickCounter exception, msg:{}", e.getMessage(), e);
            }
            return false;
        }
    

    踩坑二三事

    1. 只能对counter字段进行加减操作

    如果对非counter字段进行加减操作,报错如下:

    Query; CQL [update user_category set click_cnt = click_cnt + ? where user_id = ? and category_id = ?]; Invalid operation (click_cnt = click_cnt + ?) for non counter column click_cnt; nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Invalid operation (click_cnt = click_cnt + ?) for non counter column click_cnt
    

    2. 参数位置要求

    对于 click_cnt = click_cnt + :cnt 如果参数放在前面,即click_cnt = :cnt + click_cnt. 如下语句 update user_category set click_cnt = :cnt + click_cnt where user_id = 'user1' and category_id = 1, 则报错:

    Query; CQL [update user_category set click_cnt = ? + click_cnt  where user_id = ? and category_id = ?]; Invalid operation (click_cnt = ? - click_cnt) for non list column click_cnt; nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Invalid operation (click_cnt = ? - click_cnt) for non list column click_cnt
    

    4. counter操作的参数需为long型

    如下,将方法中参数cnt定义为Integer:

        @Query(value = "update user_category set click_cnt = click_cnt + :cnt where user_id = 'user1' and category_id = 1")
        public void incClickCnt(@Param("cnt") Integer cnt);
    

    报错如下:

    Query; CQL [update user_category set click_cnt = click_cnt + ? where user_id=? and category_id = ?]; Expected 8 or 0 byte long (4); nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Expected 8 or 0 byte long (4)
    

    5. in语句不要加小括号

    如下:

        @Query(value = "update user_category set click_cnt = click_cnt + 1 where user_id='user1' and category_id in :categoryIdList")
        public void bathUpdate(@Param("categoryIdList") List<Integer> categoryIdList);
    

    若改为news_id in (:newsIdList), 则会报错:

    org.springframework.data.cassandra.CassandraInvalidQueryException: Query; CQL [update user_category set click_cnt=click_cnt+1 where user_id=? and category_id in (?)]; Expected 4 or 0 byte int (28); nested exception is com.datastax.driver.core.exceptions.InvalidQueryException: Expected 4 or 0 byte int (28 )
    

    6. 关于CassandraOperations

    如果在unit test 中直接注入CassandraOperations测试,会报JmxReporter相关的错。但是如果在业务代码中直接注入,不会有问题。如果需要在单测中使用CassandraOperations,可以手动build一个Cluster,并禁用 JmxReporter 和 metrics。

    参考:

    Cassandra 计数器counter类型和它的限制
    Cassandra数据建模

    最后:
    没有很多时间逐个搞清楚每个问题的深层次原因啦,记录下踩的坑,以鉴他人~

  • 相关阅读:
    文本域光标操作(选、添、删、取)的jQuery扩展
    jQuery插件,将内容插入到光标处
    onmouseout,mouseover经过子元素也触发的问题解决方案
    【M4】非必要不提供default 构造方法
    【M3】绝对不要以多态方式处理数组
    100亿个数字找出最大的10个
    【M2】最好使用C++转型操作符
    【M26】限制某个class所能产生的对象数量
    理解extern
    变量的属性
  • 原文地址:https://www.cnblogs.com/shoren/p/14478218.html
Copyright © 2011-2022 走看看