如何在Corda中创建类Spring风格服务存储非Vault节点数据库数据
我来帮你搞定在Corda里实现Spring风格Repository服务的事儿,一步步来,保证清晰:
核心思路:用
@CordaService封装节点内数据访问逻辑 首先明确:@CordaService是节点级的单例服务,运行在Corda节点内部,不是客户端或Flow代码里。它可以直接访问节点的内置数据库(默认H2,可配置为PostgreSQL等),完美满足你避开Vault存储中间数据的需求。
1. 定义数据库实体类(对应数据库表)
先写你的Order实体,要符合JPA规范(Corda底层用Hibernate管理数据库),同时加上Corda的序列化注解保证RPC传输可用:
import net.corda.core.serialization.CordaSerializable; import javax.persistence.*; import java.time.LocalDateTime; @CordaSerializable @Entity @Table(name = "ORDERS") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String orderNumber; private Double amount; private LocalDateTime createdAt; // JPA必需的无参构造 public Order() {} // 业务构造方法 public Order(String orderNumber, Double amount) { this.orderNumber = orderNumber; this.amount = amount; this.createdAt = LocalDateTime.now(); } // 自行补充getter/setter方法 }
2. 实现类Spring风格的Repository
Corda支持直接用EntityManager操作数据库,我们可以封装成类似Spring Data的Repository结构,避免重复代码:
import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; public class OrderRepository { @PersistenceContext private EntityManager entityManager; @Transactional public Order save(Order order) { if (order.getId() == null) { entityManager.persist(order); return order; } else { return entityManager.merge(order); } } public Order findById(Long id) { return entityManager.find(Order.class, id); } public Order findByOrderNumber(String orderNumber) { return entityManager.createQuery( "SELECT o FROM Order o WHERE o.orderNumber = :orderNumber", Order.class) .setParameter("orderNumber", orderNumber) .getSingleResult(); } }
3. 用@CordaService封装业务服务
把Repository注入到CordaService中,作为节点内的核心服务,确保它是单例且能被RPC调用:
import net.corda.core.node.CordaService; import net.corda.core.serialization.SingletonSerializeAsToken; import javax.persistence.PersistenceContext; import javax.persistence.EntityManager; @CordaService public class OrderService extends SingletonSerializeAsToken { @PersistenceContext private EntityManager entityManager; private final OrderRepository orderRepository; public OrderService() { this.orderRepository = new OrderRepository(); // 手动注入EntityManager到Repository(Corda的注解仅对CordaService生效) // 这里用反射或者直接把Repository逻辑写在Service里也可以 try { java.lang.reflect.Field field = OrderRepository.class.getDeclaredField("entityManager"); field.setAccessible(true); field.set(orderRepository, entityManager); } catch (Exception e) { throw new RuntimeException("Failed to inject EntityManager into OrderRepository", e); } } // 暴露给RPC调用的方法 public Order saveOrder(Order order) { return orderRepository.save(order); } public Order getOrderById(Long id) { return orderRepository.findById(id); } }
注意:
@CordaService必须继承SingletonSerializeAsToken,保证节点重启后服务状态的一致性。
4. 客户端REST Controller调用CordaService
你的REST Controller是运行在客户端应用里的,通过CordaRPCOps连接节点并调用服务方法:
import net.corda.core.messaging.CordaRPCOps; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/corda-api") public class OrderController { private final CordaRPCOps proxy; // 构造注入CordaRPCOps(可通过Spring配置从节点RPC地址获取) public OrderController(CordaRPCOps proxy) { this.proxy = proxy; } @PostMapping("/orders") public Order addOrder(@RequestBody Order order) { // 通过RPC获取节点上的OrderService实例 OrderService orderService = proxy.getAppService(OrderService.class); return orderService.saveOrder(order); } @GetMapping("/orders/{id}") public Order getOrder(@PathVariable Long id) { OrderService orderService = proxy.getAppService(OrderService.class); return orderService.getOrderById(id); } }
关键注意事项
- 代码位置:
@CordaService、实体类、Repository必须放在cordapp模块(节点的业务代码模块)里,客户端代码只负责通过RPC调用。 - 序列化:所有RPC传输的对象必须加
@CordaSerializable注解,否则会序列化失败。 - 事务管理:数据库操作必须加
@Transactional注解,Corda会自动处理事务提交/回滚。 - 数据库配置:节点的数据库在
node.conf中配置,Corda会自动扫描@Entity注解的类生成表结构,无需额外配置Hibernate文件。
内容的提问来源于stack exchange,提问作者elmodeer
相关产品推荐
相关产品推荐

