gpt4 book ai didi

java - Java 中的数据原子 "update"

转载 作者:行者123 更新时间:2023-11-30 10:52:08 24 4
gpt4 key购买 nike

我用 Java 做我的项目(后端)。我不想改用 Clojure(反正现在还不想)。

不过,Datomic 看起来很有趣并且它声明它有一个 Java API,但我仍然有几个悬而未决的问题,最重要的是这个。

举个例子,假设我们有一个具有业务属性名称、电子邮件和电话的客户实体。所以在 Java 中,我们有类似的东西:

public class Customer {
private Long id;
private String name;
private String email;
private String phone;
private Long version; // ? - see 4. below
// getters, setter, toString, hashCode, equals, business logic, etc.
}

Datomic schema 声明了相应的属性:customer/name, :customer/email, :customer/phone, etc.

有一个“编辑客户”表单显示要更改的用户的 3 个业务属性。假设我更改了姓名和电子邮件并保存了表格。

现在,我应该怎么做才能将更改保存到 Datomic 中?我如何建立交易?

Datomic 提供的示例过于简单,CompareAndSwap最接近但根本没有帮助的例子。我进行了谷歌搜索,但无济于事。

答案应该是:

  1. 包含真正的 Java(不是 Clojure)代码,直到调用 connection.transact。
  2. 可重复使用/不需要为其他实体复制和粘贴。
  3. 只更新已​​更改的属性(?)- 我明白我应该只处理值实际已更改的属性(正确吗?)。
  4. 正确解决多个用户同时编辑的问题,即用户不应覆盖彼此的工作。这通常通过乐观锁定来解决。那么如何在 Java 的 Datomic 中进行乐观锁定呢?还是有其他策略?

(最后,旁注 - 不是问题本身的一部分。为什么像“编辑实体”这样的核心用例没有在 Datomic Java 文档中解释,也没有官方示例说明如何处理这个问题最好的方法?这种感觉“Datomic Java API”并没有得到真正的支持。在我看来,Java 和 Clojure 在不同的范例上工作,所以简单地将 Clojure API 1:1 移植到 Java 还不构成 Java API。难道我不能稍微注释一下客户(比如 @Id@Version)然后调用 connection.persist(customer); 完成了吗?我知道,可怕的 ORM 龙再次抬起它丑陋的头。但是,嘿,也许现在我会学习如何以更优雅的方式做到这一点。)

最佳答案

回答您的一些问题:

  1. 不必只处理已更改的字段,Datomic 会在事务运行时为您省略未更改的属性。
  2. Datomic 不提供类映射层,而且可能永远不会。我不知道社区中已经开发了一个,这也不让我感到惊讶,因为这个社区出于通用性的考虑倾向于支持面向数据(而不是基于类)的体系结构。因此,您不会找到将 Datomic 数据通用地转换为 POJO 的实用程序,例如 ORM 所提供的那样。
  3. 这并不意味着 Java 在这里完全是二等公民 - 但 Datomic 会迫使您(如果您要求创建者是为了您好)使用列表和映射等数据结构而不是 POJO 来传达信息。这在 Clojure 中确实比在 Java 中更加地道。
  4. 我个人强烈建议为您的业务逻辑使用实体(即 datomic.Entity 类的实例)而不是 POJO - 至少在编写映射之前尝试一下看看这是否是个问题代码。您失去一些静态保证 - 并且可能有很多样板。尽管如此,下面的实现确实使用了 POJO。

我在下面给了你我的实现。基本上,您将 Customer 对象转换为事务映射,并使用 :db.fn/cas 事务函数来获得更新所需的并发保证。

如果您是一位经验丰富的 Java 开发人员,这对您来说可能看起来非常不优雅 - 我知道那种感觉。同样,这并不意味着您不能从 Java 中获得 Datomic 的好处。您是否可以遵守面向数据的 API 取决于您,并且这个问题不是 Datomic 特有的 - 尽管 Datomic 倾向于迫使您转向面向数据,例如通过实体。

import datomic.*;

import java.util.List;
import java.util.Map;

public class DatomicUpdateExample {

// converts an Entity to a Customer POJO
static Customer customerFromEntity(Entity e){
if(e == null || (e.get(":customer/id") == null)){
throw new IllegalArgumentException("What you gave me is not a Customer entity.");
}
Customer cust = new Customer();
cust.setId((Long) e.get(":customer/id"));
cust.setName((String) e.get(":customer/name"));
cust.setEmail((String) e.get(":customer/email"));
cust.setPhone((String) e.get(":customer/phone"));
cust.setVersion((Long) e.get(":model/version"));
return cust;
}

// finds a Customer by
static Customer findCustomer(Database db, Object lookupRef){
return customerFromEntity(db.entity(lookupRef));
}

static List txUpdateCustomer(Database db, Customer newCustData){
long custId = newCustData.getId();
Object custLookupRef = Util.list(":customer/id", custId);
Customer oldCust = findCustomer(db, custLookupRef); // find old customer by id, using a lookup ref on the :customer.id field.
long lastKnownVersion = oldCust.getVersion();
long newVersion = lastKnownVersion + 1;
return Util.list( // transaction data is a list
Util.map( // using a map is convenient for updates
":db/id", Peer.tempid(":db.part/user"),
":customer/id", newCustData.getId(), // because :customer/id is a db.unique/identity attribute, this will map will result in an update
":customer/email", newCustData.getEmail(),
":customer/name", newCustData.getName(),
":customer/phone", newCustData.getPhone()
),
// 'Compare And Swap': this clause will prevent the update from happening if other updates have occurred by the time the transaction is executed.
Util.list(":db.fn/cas", custLookupRef, ":model/version", lastKnownVersion, newVersion)
);
}

static void updateCustomer(Connection conn, Customer newCustData){
try {
Map txResult = conn.transact(txUpdateCustomer(conn.db(), newCustData)).get();
} catch (InterruptedException e) {
// TODO deal with it
e.printStackTrace();
} catch (Exception e) {
// if the CAS failed, this is where you'll know
e.printStackTrace();
}
}
}

class Customer {
private Long id;
private String name;
private String email;
private String phone;
private Long version;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getPhone() {
return phone;
}

public void setPhone(String phone) {
this.phone = phone;
}

public Long getVersion() {
return version;
}

public void setVersion(Long version) {
this.version = version;
}
}

关于java - Java 中的数据原子 "update",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34470318/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com