背景
作为面向大数据场景的半结构化、结构化存储系统,Lindorm已经在阿里发展了近十年,并始终保持着快速的能力更新和技术升级,是目前支撑阿里经济体业务的核心数据库产品之一。其在功能、性能、稳定性等方面的诸多创新历经了长时间的大规模实践考验,被全面应用于阿里集团、蚂蚁集团、菜鸟、大文娱等各个业务板块,成为目前为止公司内部数据体量最大、覆盖业务最广的数据库产品。
Lindorm Serverless 支持按需计费,弹性扩展,最大限度的降低了用户使用门槛,运维难度和容量规划操作。Lindorm CQL完全兼容了cassandra社区的Query Language,提供了类SQL的查询能力,可以非常快速的结合spring等框架,帮助业务逻辑快速开发。本文以一个简单的用户管理系统为例子,介绍如何给予Lindorm快速构建一个web服务,这个系列后续也会介绍更多复杂的使用方式。
前期准备工作
公有云用户
在阿里云首页搜索Lindorm,进入Lindorm管理控制台购买Lindorm Serverless集群,可以根据需求选择包年包月或者按量付费。
已经有集群版实例子的同学可以跳过这个步骤。
购买成功之后,实例列表就包含了一个LindormServerless类型的实例在实例管理页面找到连接信息页面,开通公网访问,获取集群链接串。
链接信息中默认都存在"CQL连接",默认的链接地址是包括了VPC环境下的地址和端口,也可以执行公网开放功能获取公网地址和端口。Lindorm使用CQL访问都是走认证的,默认用户名和默认密码在用户控制台可以看到,通过该地方获取的用户名和密码可以访问走CQL访问Lindorm。
至此Lindorm的集群的购买工作就已经完成了。
了解Cqlsh
执行建表和KeySpace操作用户可以通过cqlsh访问lindorm serverless,操作方式请参考cqlsh使用指南。
下载到CQLSH的代码后,可以通过如下命令访问Shell
./cqlsh $host $port -u $username -p $password
其中host指的是CQL链接串中:9042之前的部分,port不填写默认为9042
创建namespace和table
cqlsh> CREATE KEYSPACE test_keyspace WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} AND durable_writes = true;
cqlsh> use test_keyspace;
cqlsh:test_keyspace> CREATE TABLE user (id BIGINT , username text, age int, password text, PRIMARY KEY (id)) ;
对应的CQL表结构
CREATE TABLE test_keyspace.user (
id bigint PRIMARY KEY,
username text,
age int,
password text);
生成SpringBoot项目
在开始实际编码之前,建议先阅读下官方文档,了解Spring Data for Cassandra项目
在Spring模版生成器中生成项目模版,所需配置如下图:
生成出来的项目下载后导入进Idea,大概如下
现在我们就拥有了一个完整spring on cql,接下来的工作就是完善代码构造一个用户管理页面啦。
构造用户管理页面
定义域模型(实体类)
本文主要针对单个用户主键场景,所有的读写操作都是针对主键ID来执行,注意主键需要标注PrimaryKey。用户ID作为主键是全局唯一的,重复写入一个用户ID会发生覆盖。
/*
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.entity;
import org.springframework.data.cassandra.core.mapping.CassandraType;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
@Table(value = "user")
public class UserTable {
@PrimaryKey
@CassandraType(type = CassandraType.Name.BIGINT)
private long id;
private String username;
private String password;
private int age;
public long getId() {
return id;
}
public UserTable setId(long id) {
this.id = id;
return this;
}
public String getUserName() {
return username;
}
public UserTable setUserName(String username) {
this.username = username;
return this;
}
public String getPassword() {
return password;
}
public UserTable setPassword(String password) {
this.password = password;
return this;
}
public int getAge() {
return age;
}
public UserTable setAge(int age) {
this.age = age;
return this;
}
}
定义spring-data接口
这里主要继承了CrudRepository,包括了比较常见的增删改查
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.entity;
package com.alibaba.lindorm.cqldemo.repository;
import com.alibaba.lindorm.cqldemo.entity.UserTable;import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<UserTable, Long> {
UserTable findById(long id);
void deleteById(Long id);
}
CrudRepository的接口定义如下
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S var1);
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
Optional<T> findById(ID var1);
boolean existsById(ID var1);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> var1);
long count();
void deleteById(ID var1);
void delete(T var1);
void deleteAll(Iterable<? extends T> var1);
void deleteAll();
}
定义service
咱们这个简单的demo代码支持拿到用户列表,写入用户信息,更新用户信息,删除用户等基本功能,其支持的基本功能如图。
/*
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.service;
import com.alibaba.lindorm.cqldemo.entity.UserTable;
import java.util.List;
public interface UserService {
public List<UserTable> getUserList();
public UserTable findUserById(long id);
public void save(UserTable userTable);
public void edit(UserTable userTable);
public void delete(long id);
}
/*
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.service;
import com.alibaba.lindorm.cqldemo.entity.UserTable;
import com.alibaba.lindorm.cqldemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserRepository userRepository;
@Override
public List<UserTable> getUserList() {
List<UserTable> userList = new ArrayList<>();
for (UserTable table : userRepository.findAll()) {
userList.add(table);
}
return userList;
}
@Override
public UserTable findUserById(long id) {
return userRepository.findById(id);
}
@Override
public void save(UserTable userTable) {
userRepository.save(userTable);
}
@Override
public void edit(UserTable userTable) {
userRepository.save(userTable);
}
@Override
public void delete(long id) {
userRepository.deleteById(id);
}
}
完成webcontroller
UserController实现基本的跳转功能,将咱们的前段页面跟service关联起来。
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.web;
import com.alibaba.lindorm.cqldemo.entity.UserTable;
import com.alibaba.lindorm.cqldemo.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import java.util.List;
@Controller
public class UserController {
@Resource UserService userService;
@RequestMapping("/")
public String index() {
return "redirect:/list";
}
@RequestMapping("/list")
public String list(Model model) {
List<UserTable> userTables = userService.getUserList();
model.addAttribute("userTables", userTables);
return "user/list";
}
@RequestMapping("/toAdd")
public String toAdd() {
return "user/userAdd";
}
@RequestMapping("/add")
public String add(UserTable userTable) {
userService.save(userTable);
return "redirect:/list";
}
@RequestMapping("/toEdit")
public String toEdit(Model model,Long id) {
UserTable userTable = userService.findUserById(id);
model.addAttribute("userTable", userTable);
return "user/userEdit";
}
@RequestMapping("/edit")
public String edit(UserTable userTable) {
userService.edit(userTable);
return "redirect:/list";
}
@RequestMapping("/delete")
public String delete(Long id) {
userService.delete(id);
return "redirect:/list";
}
}
完成前端页面
前端页面的具体代码,请参考具体的示例代码。总共实现了3个页面,list.html负责用户展示,userAdd.html负责用户添加,userEdit.html负责用户更新页面。
完成main函数
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cql.springdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class CqlDemoApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(CqlDemoApplication.class);
}
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.class.path"));
SpringApplication.run(CqlDemoApplication.class, args);
}
}
增加cassandra链接参数
通过继承AbstractCassandraConfiguration来指定链接的EndPoint,用户名,密码等信息。用户名密码可以通过阿里云管控系统或者Afanty上获取。
* Copyright Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
package com.alibaba.lindorm.cqldemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
import org.springframework.data.cassandra.config.CqlSessionFactoryBean;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
@Configuration
@EnableCassandraRepositories
public class CassandraConfig extends AbstractCassandraConfiguration {
private String hostList = "ld-bp1r9jd815x6y8w4d-proxy-lindorm-pub.lindorm.rds.aliyuncs.com";
private String keyspaceName = "test_keyspace";
//需要填写自己集群的对应账号
private String userName = "xxx";
//需要填写自己集群的对应密码
private String password = "xxx";
@Override
protected String getLocalDataCenter() {
return "datacenter1";
}
protected String getContactPoints() {
return hostList;
}
public String getKeyspaceName() {
return keyspaceName;
}
@Override
public CqlSessionFactoryBean cassandraSession() {
CqlSessionFactoryBean cqlSessionFactoryBean = super.cassandraSession();
cqlSessionFactoryBean.setPassword(password);
cqlSessionFactoryBean.setUsername(userName);
return cqlSessionFactoryBean;
}
}
当然,你也可以通过更改application.properties调整cassandra参数,需要注意contact-points, username,password这几个参数是必须填写的,详细的参数请参考spring-cassandra的官方文档。
启动查看
执行main函数,如果一切符合的预期的话,我们的webserver已经可以正常拉起,可以在本机localhost:8080端口访问这个简易的用户管理系统。
我们可以测试添加一个新的用户3,也可以修改删除和删除某个用户,一个拥有最基础的功能的用户管理系统就已经完成啦!
增加css修饰网页
我们的页面还没有经过任何修饰,感觉很粗糙,在resources/static/css目录加入bootstrap.css文件,修饰下我们的网页,使我们的网页变得更加美观!
更多高级功能
本文只涉及了单一主键场景下基于主键进行增删改查的简单例子。实际上spring + cql + lindorm serverless还可以支持更加强大的能力,比如复合主键,二级索引,多维索引等。想了解更多如何关于Lindorm的能力,请参考Lindorm使用文档 https://help.aliyun.com/product/172543.html?spm=5176.15155444.J_5253785160.4.28925d80aDpnwq。 也请期待本文的姊妹篇,springboot + lindorm构建用户账单系统,我们会更加详尽的介绍如何设计复合主键,二级索引等能力。
Have fun with the demo!