Faunadb和google spanner都属于云分布式数据库天然支持分片(无需做分表分库操作,一库搞定,当然价格另说),国内的也有比如TiDB Oceanbase等
本文使用java语言,其他语言可以跳过;有想直接使用的可以参考(无法访问外网,可以搞个吧!!!,有时会遇到网络问题):GitHub - fauna/faunadb-jvm: Scala and Java driver for FaunaDB v4
此文旨在想了解的小伙伴看看(免费使用30天)
本文演示使用的jdk版本为jdk21
目录
1.登录账号
2.了解一下FQL
3. 创建数据库,创建集合
4.点击搜索框中的dashbord进入到控制台然后到控制台创建集合
5. 生成数据库秘钥
6.springboot整合项目
7.实体
8.service及接口
9.属性文件配置属性
10.controller
11.启动后postman试试
使用github账号或者注册一个
Welcome to Fauna docs - Fauna Documentation
建议按照图看下去
最好是跟着官网文档走
(参考:使用 Spring Boot 使用 Fauna 和 Java 开始构建_rxjava_云O生-云原生)
最新的不一样,使用下面语法
Collection.create({
name: 'todos'
})
记住不要到account哪里去申请
依赖
实体\service\controller均参考博文:使用 Spring Boot 使用 Fauna 和 Java 开始构建_rxjava_云O生-云原生
参考这个也行:GitHub - fauna/faunadb-jvm: Scala and Java driver for FaunaDB v4
import lombok.Data;
@Data
public abstract class Entity {
protected String id;
}
import java.util.List;
import java.util.Optional;
public class Page {
private List data;
private Optional before;
private Optional after;
public Page(List data, Optional before, Optional after) {
this.data = data;
this.before = before;
this.after = after;
}
public List getData() {
return data;
}
public void setData(List data) {
this.data = data;
}
public Optional getBefore() {
return before;
}
public void setBefore(Optional before) {
this.before = before;
}
public Optional getAfter() {
return after;
}
public void setAfter(Optional after) {
this.after = after;
}
}
package com.rulecheck.entity;
import java.util.Optional;
public class PaginationOptions {
private Optional size;
private Optional before;
private Optional after;
public PaginationOptions(Optional size, Optional before, Optional after) {
this.size = size;
this.before = before;
this.after = after;
}
public Optional getSize() {
return size;
}
public void setSize(Optional size) {
this.size = size;
}
public Optional getBefore() {
return before;
}
public void setBefore(Optional before) {
this.before = before;
}
public Optional getAfter() {
return after;
}
public void setAfter(Optional after) {
this.after = after;
}
}
import com.faunadb.client.types.FaunaConstructor;
import com.faunadb.client.types.FaunaField;
public class TodoEntity extends Entity {
@FaunaField
private String title;
@FaunaField
private String description;
@FaunaConstructor
public TodoEntity(@FaunaField("id") String id,
@FaunaField("title") String title,
@FaunaField("description") String description) {
this.id = id;
this.title = title;
this.description = description;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
package com.rulecheck.entity;
public class CreateOrUpdateTodoData {
private String title;
private String description;
public CreateOrUpdateTodoData(String title, String description) {
this.title = title;
this.description = description;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
package com.rulecheck.service;
import com.faunadb.client.FaunaClient;
import com.faunadb.client.errors.NotFoundException;
import com.faunadb.client.query.Expr;
import com.faunadb.client.query.Pagination;
import com.faunadb.client.types.Value;
import com.rulecheck.entity.Entity;
import com.rulecheck.entity.Page;
import com.rulecheck.entity.PaginationOptions;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.faunadb.client.query.Language.*;
import java.lang.Class;
public abstract class FaunaRepository implements Repository, IdentityFactory {
@Resource
private FaunaClient faunaClient;
protected final Class entityType;
protected final String collectionName;
protected final String collectionIndexName;
protected FaunaRepository(Class entityType, String collectionName, String collectionIndexName) {
this.entityType = entityType;
this.collectionName = collectionName;
this.collectionIndexName = collectionIndexName;
}
// This method returns a unique valid Id leveraging Fauna's NewId function.
@Override
public CompletableFuture nextId() {
CompletableFuture result =
faunaClient.query(
NewId()
)
.thenApply(value -> value.to(String.class).get());
return result;
}
// This method saves an entity to the database using the saveQuery method below. It also returns the result of the saved entity.
@Override
public CompletableFuture save(T entity) {
CompletableFuture result =
faunaClient.query(
saveQuery(Value(entity.getId()), Value(entity))
)
.thenApply(this::toEntity);
return result;
}
// This method deletes from the data an entity(document) with the specified Id.
@Override
public CompletableFuture> remove(String id) {
CompletableFuture result =
faunaClient.query(
Select(
Value("data"),
Delete(Ref(Collection(collectionName), Value(id)))
)
)
.thenApply(this::toEntity);
CompletableFuture> optionalResult = toOptionalResult(result);
return optionalResult;
}
// This method finds an entity by its Id and returns the entity result.
@Override
public CompletableFuture> find(String id) {
CompletableFuture result =
faunaClient.query(
Select(
Value("data"),
Get(Ref(Collection(collectionName), Value(id)))
)
)
.thenApply(this::toEntity);
CompletableFuture> optionalResult = toOptionalResult(result);
return optionalResult;
}
// This method returns all entities(documents) in the database collection using the paginationOptions parameters.
@Override
public CompletableFuture> findAll(PaginationOptions po) {
Pagination paginationQuery = Paginate(Match(Index(Value(collectionIndexName))));
po.getSize().ifPresent(size -> paginationQuery.size(size));
po.getAfter().ifPresent(after -> paginationQuery.after(Ref(Collection(collectionName), Value(after))));
po.getBefore().ifPresent(before -> paginationQuery.before(Ref(Collection(collectionName), Value(before))));
CompletableFuture> result =
faunaClient.query(
Map(
paginationQuery,
Lambda(Value("nextRef"), Select(Value("data"), Get(Var("nextRef"))))
)
).thenApply(this::toPage);
return result;
}
// This is the saveQuery expression method used by the save method to persist the database.
protected Expr saveQuery(Expr id, Expr data) {
Expr query =
Select(
Value("data"),
If(
Exists(Ref(Collection(collectionName), id)),
Replace(Ref(Collection(collectionName), id), Obj("data", data)),
Create(Ref(Collection(collectionName), id), Obj("data", data))
)
);
return query;
}
// This method converts a FaunaDB Value into an Entity.
protected T toEntity(Value value) {
return value.to(entityType).get();
}
// This method returns an optionalResult from a CompletableFuture result.
protected CompletableFuture> toOptionalResult(CompletableFuture result) {
CompletableFuture> optionalResult =
result.handle((v, t) -> {
CompletableFuture> r = new CompletableFuture<>();
if(v != null) r.complete(Optional.of(v));
else if(t != null && t.getCause() instanceof NotFoundException) r.complete(Optional.empty());
else r.completeExceptionally(t);
return r;
}).thenCompose(Function.identity());
return optionalResult;
}
// This method converts a FaunaDB Value into a Page with the Entity type.
protected Page toPage(Value value) {
Optional after = value.at("after").asCollectionOf(Value.RefV.class).map(c -> c.iterator().next().getId()).getOptional();
Optional before = value.at("before").asCollectionOf(Value.RefV.class).map(c -> c.iterator().next().getId()).getOptional();
List data = value.at("data").collect(entityType).stream().collect(Collectors.toList());
Page page = new Page(data, before, after);
return page;
}
}
import java.util.concurrent.CompletableFuture;
public interface IdentityFactory {
CompletableFuture nextId();
}
import com.rulecheck.entity.Entity;
import com.rulecheck.entity.Page;
import com.rulecheck.entity.PaginationOptions;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public interface Repository {
// This method saves the given Entity into the Repository.
CompletableFuture save(T entity);
// This method finds an Entity for the given Id
CompletableFuture> find(String id);
// This method retrieves a Page of TodoEntity entities for the given PaginationOptions
CompletableFuture> findAll(PaginationOptions po);
// This method finds the Entity for the given Id and removes it. If no Entity can be found for the given Id an empty result is returned.
CompletableFuture> remove(String id);
}
import com.rulecheck.entity.TodoEntity;
import org.springframework.stereotype.Repository;
@Repository
public class TodoRepository extends FaunaRepository {
public TodoRepository(){
super(TodoEntity.class, "todos", "all_todos");
}
//-- Custom repository operations specific to the TodoEntity will go below --//
}
import com.rulecheck.entity.CreateOrUpdateTodoData;
import com.rulecheck.entity.Page;
import com.rulecheck.entity.PaginationOptions;
import com.rulecheck.entity.TodoEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Service
public class TodoService {
@Autowired
private TodoRepository todoRepository;
public CompletableFuture createTodo(CreateOrUpdateTodoData data) {
CompletableFuture result =
todoRepository.nextId()
.thenApply(id -> new TodoEntity(id, data.getTitle(), data.getDescription()))
.thenCompose(todoEntity -> todoRepository.save(todoEntity));
return result;
}
public CompletableFuture> getTodo(String id) {
return todoRepository.find(id);
}
public CompletableFuture> updateTodo(String id, CreateOrUpdateTodoData data) {
CompletableFuture> result =
todoRepository.find(id)
.thenCompose(optionalTodoEntity ->
optionalTodoEntity
.map(todoEntity -> todoRepository.save(new TodoEntity(id, data.getTitle(), data.getDescription())).thenApply(Optional::of))
.orElseGet(() -> CompletableFuture.completedFuture(Optional.empty())));
return result;
}
public CompletableFuture> deleteTodo(String id) {
return todoRepository.remove(id);
}
public CompletableFuture> getAllTodos(PaginationOptions po) {
return todoRepository.findAll(po);
}
}
import com.faunadb.client.FaunaClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
/**
* description:
*/
@Slf4j
@SpringBootApplication
public class RuleCheckApplication {
@Value("${fauna-db.secret}")
private String serverKey;
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public FaunaClient faunaConfiguration() {
log.info("serverKey:{}", serverKey);
FaunaClient faunaClient = FaunaClient.builder()
.withSecret(serverKey)
.build();
return faunaClient;
}
public static void main(String[] args) {
SpringApplication.run(RuleCheckApplication.class, args);
}
}
fauna-db.secret=fnAFN6K4SxAAQWm......... 自己生成数据库秘钥,非账户秘钥或者密码
package com.rulecheck.controller;
import com.rulecheck.entity.CreateOrUpdateTodoData;
import com.rulecheck.entity.Page;
import com.rulecheck.entity.PaginationOptions;
import com.rulecheck.entity.TodoEntity;
import com.rulecheck.service.TodoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@RestController
public class TodoRestController {
@Autowired
private TodoService todoService;
@PostMapping("/todos")
public CompletableFuture createTodo(@RequestBody CreateOrUpdateTodoData data) {
return todoService.createTodo(data)
.thenApply(todoEntity -> new ResponseEntity(todoEntity, HttpStatus.CREATED));
}
@GetMapping("/todos/{id}")
public CompletableFuture getTodo(@PathVariable("id") String id) {
CompletableFuture result =
todoService.getTodo(id)
.thenApply(optionalTodoEntity ->
optionalTodoEntity
.map(todoEntity -> new ResponseEntity(todoEntity, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity(HttpStatus.NOT_FOUND))
);
return result;
}
@PutMapping("/todos/{id}")
public CompletableFuture updateTodo(@PathVariable("id") String id, @RequestBody CreateOrUpdateTodoData data) {
CompletableFuture result =
todoService.updateTodo(id, data)
.thenApply(optionalTodoEntity ->
optionalTodoEntity
.map(todoEntity -> new ResponseEntity(todoEntity, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity(HttpStatus.NOT_FOUND)
)
);
return result;
}
@DeleteMapping(value = "/todos/{id}")
public CompletableFuture deletePost(@PathVariable("id")String id) {
CompletableFuture result =
todoService.deleteTodo(id)
.thenApply(optionalTodoEntity ->
optionalTodoEntity
.map(todo -> new ResponseEntity(todo, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity(HttpStatus.NOT_FOUND)
)
);
return result;
}
@GetMapping("/todos")
public CompletableFuture> getAllTodos(
@RequestParam("size") Optional size,
@RequestParam("before") Optional before,
@RequestParam("after") Optional after) {
PaginationOptions po = new PaginationOptions(size, before, after);
CompletableFuture> result = todoService.getAllTodos(po);
return result;
}
}
11. 参考pom依赖
org.springframework.boot
spring-boot-starter-parent
3.1.3
8
8
UTF-8
3.5.3.1
8.0.28
2.11.0
org.springframework.boot
spring-boot-starter-web
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus-boot-starter}
mysql
mysql-connector-java
${mysql-connector-java}
com.baomidou
mybatis-plus-generator
${mybatis-plus-boot-starter}
org.springframework.boot
spring-boot-starter-data-redis
com.github.ben-manes.caffeine
caffeine
commons-io
commons-io
${commons-io}
org.projectlombok
lombok
1.18.28
provided
com.faunadb
faunadb-java
4.4.0
compile
http://localhost:8080/todos
{
"title":"create a job",
"description":"this post request is todos"
}