我和我的团队正在努力升级我们公司的系统,因为它被遗忘了,并且正在运行它使用的所有东西的旧版本;因此,开发更新的功能正在成为新技术和不支持技术的痛苦。
到目前为止,我们已经设法生成了一个几乎完全可用的系统版本;但是我们陷入了一个涉及Datanucleus-JDO,MongoDB和继承的功能。
我们有一些非常类似的模型(来自代码的预期)。在当前的生产中版本中,对它应用更改通常涉及在所有类中重写相同的代码段,因此我们认为继承将使工作更容易和更好。所以我们在顶级层次上有两个接口(据我们所知,Datanuclues和MongoDB根本不关心它们);这是这样的:
public interface Entity extends Serializable {
String getDate();
double getQty();
void setQty(double qty);
void setDate(String date);
void setKey(Key key);
}
和
public interface HourEntity extends Entity {
String getHour();
}
我们使用应用程序定义的键,我们使用这个唯一的类来构建不同类型的键。我们只希望此类的toString表示在Mongo中进行sotre和检索数据。
public final class Key implements Serializable {
static final long serialVersionUID = -448150158203091507L;
public final String targetClassName;
public final String id;
public final String toString;
public final int hashCode;
public Key() {
targetClassName = null;
id = null;
toString = null;
hashCode = -1;
}
public Key(String str) {
String[] parts = str.split("\\(");
parts[1] = parts[1].replaceAll("\\)", " ");
parts[1] = parts[1].replace("\"", " ");
parts[1] = parts[1].trim();
this.targetClassName = parts[0];
this.id = parts[1];
toString = this.toString();
hashCode = this.hashCode();
}
public Key(String classCollectionName, String id) {
if (StringUtils.isEmpty(classCollectionName)) {
throw new IllegalArgumentException("No collection/class name specified.");
}
if (id == null) {
throw new IllegalArgumentException("ID cannot be null");
}
targetClassName = classCollectionName;
this.id = id;
toString = this.toString();
hashCode = this.hashCode();
}
public String getTargetClassName() {
return targetClassName;
}
public int hashCode() {
if(hashCode != -1) return hashCode;
int prime = 31;
int result = 1;
result = prime * result + (id != null ? id.hashCode() : 0);
result = prime * result + (targetClassName != null ? targetClassName.hashCode() : 0);
return result;
}
public boolean equals(Object object) {
if (object instanceof Key) {
Key key = (Key) object;
if (this == key)
return true;
return targetClassName.equals(key.targetClassName) && Objects.equals(id, key.id);
} else {
return false;
}
}
public String toString() {
if(toString != null) return toString;
StringBuilder buffer = new StringBuilder();
buffer.append(targetClassName);
buffer.append("(");
if (id != null) {
buffer.append((new StringBuilder()).append("\"").append(id)
.append("\"").toString());
} else {
buffer.append("no-id-yet");
}
buffer.append(")");
return buffer.toString();
}
}
这个apllication定义的标识在所有其他不涉及iheritance的模型上都能正常工作。
这是我们打算存储在数据存储区中的实际模型之一:
@PersistenceCapable(detachable="true")
@Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class Ticket implements Entity {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.UNSPECIFIED, column="_id")
protected Key key;
protected String date;
protected int qty;
public Ticket() {
this.qty = 0;
}
public Key getKey() {
return key;
}
@Override
public void setKey(Key key) {
this.key = key;
}
public double getQty() {
return qty;
}
public void setQty(double qty) {
this.qty = (int) qty;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Ticket other = (Ticket) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
@Override
public String toString() {
return "Ticket [key=" + key + ", date=" + date + ", qty="
+ qty + "]";
}
}
这是它的子类(涉及这个问题的所有模型只涉及一个超类,每个超类只有一个子):
@PersistenceCapable(detachable="true")
@Inheritance(strategy=InheritanceStrategy.COMPLETE_TABLE)
public class HourTicket extends Ticket implements HourEntity {
private String hour;
public HourTicket() {
super();
}
public Key getKey() {
return key;
}
@Override
public void setKey(Key key) {
this.key = key;
}
public String getHour() {
return hour;
}
public void setHour(String hour) {
this.hour = hour;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HourTicket other = (HourTicket) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
@Override
public String toString() {
return "HourTicket [key=" + key + ", date=" + date
+ ", hour=" + hour + ", qty=" + qty + "]";
}
}
最后,persisntance.xml就像这样
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<!-- JOSAdmin "unit" -->
<persistence-unit name="ourdatastore">
<class>mx.ourdomain.Ticket</class>
<class>mx.ourdomain.HourTicket</class>
<exclude-unlisted-classes/>
</persistence-unit>
</persistence>
和package-mongo.orm
<?xml version="1.0"?>
<!DOCTYPE orm SYSTEM "file:/javax/jdo/orm.dtd">
<orm>
<package name="mx.ourdomain" >
<class name="Ticket" table="Ticket">
<field name="key" primary-key="true" >
<column name="_id" length="100" />
</field >
</class>
<class name="HourTicket" table="HourTicket">
<primary-key >
<column name="_id" target="_id" />
</primary-key>
</class>
</package>
</orm>
因此,当尝试使用超类或子类执行任何读取或写入操作时会出现问题。对于几个(我们所知道的所有可能)场景中的相同确切结果,这已经令人满意,但我们研究的测试场景始于此调用:
Ticket ticket = persistenceManager.getObjectById(Ticket.class, key);
使用标准程序生成密钥,其他模型使用该程序成功存储和读取;当然,它是之前显示的关键类。
我们已经远远超出了除此之外的数据核心任务。我们发现如预期的那样:
但是当试图获取类名来确定哪个是要查询的正确Mongo集合时,datanucleus-mongodb尝试查询这两个类(Ticket和HourTicket);但然后它处理mongo驱动程序键对象 perse ,然后抛出 CodecConfigurationException ,因为mongo不知道如何使用key类(构建查询时,datanucleus-mongo创建一个具有结构{_id:key}的BasicDBObject,由于键条目而无法在没有编解码器的情况下构建。这发生在MongoDBUtils类中在datanucleus-mongodb项目v5.1.0中;类MongoDBUtils,方法getClassNameForIdentity(Object,AbstractClassMetaData,ExecutionContext,ClassLoaderResolver))。
所以,我们假设我们有一些配置缺失告诉datanucleus它应该使用密钥的toString()形式;因为Monogo驱动程序处理String就好了(datanuclues docs实际上声明当使用自定义类作为数据存储区时,它将使用密钥的toString()形式;所以我不确定这是否可能是一个错误。)
我们尝试使用KeyTraslator插件并使密钥类成为DatastoreId并在StringId中包装但没有成功:触发相同的异常,除非在StringId中包装Key类:mongo讲座已成功但是然后当试图构建模型对象时,抛出ClassCastException,因为String不能被转换为Key,并且重构代码以使用String键将严重破坏数据库中已有的数据;因为它有一个特殊的格式,密钥类可以读取和生成。
使用带有datoucleus JDO w / mongoDB的继承是否有遗漏的东西?
答案 0 :(得分:0)
我没有太多关注objectIdClass元数据周围的设置;因为从我得到的文档中,它们仅用于组合键。结果是如果你定义一个只有一个属性的objectId类;然后它表现为自定义SingleFieldId;这就是我们想要的。
我发现“有趣”的事实是,未注释(或objectIdClass的非声明元数据)类可以正常工作,并且使用的自定义键将被很好地利用;但是一旦你把它们中的任何一个变成超级类,那么你就有义务添加objectIdClass元数据。
除了使用objectIdClass注释Ticket类(以及所有其他超类)之外,我们:
就是这样!随着这些微小的变化,我们的系统运作良好;其他持久性类别和DAO都不需要进行其他更改。