Java 达托米语;更新;在爪哇
我用Java做我的项目(后端)。我不想改用Clojure(至少现在还不想) 然而,Datomic看起来很有趣,并且它声明它有一个JavaAPI,但是我仍然有几个问题没有解决,最重要的是这个问题 举个例子,假设我们有一个客户实体,其业务属性为name、email和phone。因此,在Java中,我们有如下内容:Java 达托米语;更新;在爪哇,java,datomic,Java,Datomic,我用Java做我的项目(后端)。我不想改用Clojure(至少现在还不想) 然而,Datomic看起来很有趣,并且它声明它有一个JavaAPI,但是我仍然有几个问题没有解决,最重要的是这个问题 举个例子,假设我们有一个客户实体,其业务属性为name、email和phone。因此,在Java中,我们有如下内容: public class Customer { private Long id; private String name; private String email; pr
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模式声明了相应的属性:customer/name、:customer/email、:customer/phone等
有一个“编辑客户”表单,为要更改的用户公开3个业务属性。假设我更改了姓名和电子邮件,并保存了表单
现在,我应该怎么做才能将更改保存到Datomic?如何构建事务
Datomic提供的示例过于简单化,示例非常接近,但根本没有帮助。我用谷歌搜索了一下,但没有用
答案应该是:
难道我不应该给客户添加一点注释(比如@Id和@Version),然后打电话给connection.persist(客户);然后处理好它吗?我知道,可怕的ORM龙又抬起了它丑陋的头。但是,嘿,也许现在我将学习如何以一种更优雅的方式来处理它。)回答您的一些问题:
datomic.Entity
类的实例)而不是POJO——至少在编写映射代码之前尝试一下,看看这是否是一个问题。您将失去一些静态保证,并且可能会丢失很多样板文件。尽管如此,下面的实现仍然使用POJO: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;
}
}
嗨,瓦伦丁,谢谢你的回答。我有两个问题:1)我不明白为什么要在更新中创建新的tempid。我在没有它的情况下进行了更新。2) 我无法在版本不匹配的情况下抛出db.fn/cas。我已经在这里更新了我的示例项目:CustomerServiceTest.update_Conflict()测试失败。1)由于upsert行为,tempids不会受损,这就是我一直使用它们的原因。2) 您的测试不起作用,因为在您的
update()
方法中,以前的版本是根据数据库调用计算的,而不是从您作为参数传递的客户(以及您伪造的版本)计算的。2)对,我现在得出了相同的结论,但发现您已经在这里指出了这一点。因此,关于您自己的代码,最新的知识转换应该来自newCustData,而不是oldCust。我接受您的回答,因为您帮助我实现了乐观锁定。由此产生的支持基本创建、获取、更新和列表的通用Datomic存储库就在这里:我遇到的所有问题都以某种方式得到了解决。谢谢